1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
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. *
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. *
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 ***************************************************************************/
21 * Handles all CD-ROM registers and functions.
27 #include "cdrom-async.h"
31 #include "psxevents.h"
32 #include "arm_features.h"
36 #define CDR_LOG SysPrintf
41 #define CDR_LOG_I SysPrintf
43 #define CDR_LOG_I(fmt, ...) \
44 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
47 #define CDR_LOG_IO SysPrintf
49 #define CDR_LOG_IO(...)
52 //#define CDR_LOG_CMD_ACK
55 // unused members maintain savesate compatibility
56 unsigned char unused0;
57 unsigned char unused1;
58 unsigned char IrqMask;
59 unsigned char unused2;
61 unsigned char IrqStat;
65 unsigned char Transfer[DATA_SIZE];
69 unsigned char Relative[3];
70 unsigned char Absolute[3];
72 unsigned char TrackChanged;
73 unsigned char ReportDelay;
74 unsigned char PhysCdPropagations;
75 unsigned short sectorsRead;
76 unsigned int freeze_ver;
78 unsigned char Prev[4];
79 unsigned char Param[8];
80 unsigned char Result[16];
84 unsigned char ResultC;
85 unsigned char ResultP;
86 unsigned char ResultReady;
88 unsigned char SubqForwardSectors;
89 unsigned char SetlocPending;
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];
101 unsigned char FileChannelSelected;
102 unsigned char CurFile, CurChannel;
103 int FilterFile, FilterChannel;
104 unsigned char LocL[8];
115 u32 LastReadSeekCycles;
119 u8 DriveState; // enum drive_state
124 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
125 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
126 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
127 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
129 alignas(64) static s16 read_buf[CD_FRAMESIZE_RAW_ALIGNED / 2];
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];
143 /* CD-ROM magic numbers */
144 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
149 #define CdlBackward 5
157 #define CdlSetfilter 13
158 #define CdlSetmode 14
159 #define CdlGetparam 15
160 #define CdlGetlocL 16
161 #define CdlGetlocP 17
167 #define CdlSetclock 23
168 #define CdlGetclock 24
174 #define CdlReadToc 30
177 static 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
189 unsigned char Test04[] = { 0 };
190 unsigned char Test05[] = { 0 };
191 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
192 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
193 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
199 #define Acknowledge 3
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
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
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
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)
237 #define LOCL_INVALID 0xff
238 #define SUBQ_FORWARD_SECTORS 2u
241 DRIVESTATE_STANDBY = 0, // different from paused
243 DRIVESTATE_RESCAN_CD,
244 DRIVESTATE_PREPARE_CD,
247 DRIVESTATE_PLAY_READ,
251 static struct CdrStat cdr_stat;
253 static unsigned int msf2sec(const u8 *msf) {
254 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
257 // cdrPlayReadInterrupt
258 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
260 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
262 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
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_); \
269 #define StopReading() { \
271 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
274 #define StopCdda() { \
276 cdr.FastForward = 0; \
277 cdr.FastBackward = 0; \
280 #define SetPlaySeekRead(x, f) { \
281 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
285 #define SetResultSize_(size) { \
287 cdr.ResultC = size; \
288 cdr.ResultReady = 1; \
291 #define SetResultSize(size) { \
292 if (cdr.ResultP < cdr.ResultC) \
293 CDR_LOG_I("overwriting result, len=%u\n", cdr.ResultC); \
294 SetResultSize_(size); \
297 static void setIrq(u8 irq, int log_cmd)
299 u8 old = cdr.IrqStat & cdr.IrqMask ? 1 : 0;
300 u8 new_ = irq & cdr.IrqMask ? 1 : 0;
303 if ((old ^ new_) & new_)
304 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
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]);
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)
321 void cdrLidSeekInterrupt(void)
323 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
325 switch (cdr.DriveState) {
327 case DRIVESTATE_STANDBY:
330 SetPlaySeekRead(cdr.StatP, 0);
332 if (cdra_getStatus(&cdr_stat) == -1)
335 if (cdr_stat.Status & STATUS_SHELLOPEN)
337 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
338 cdr.DriveState = DRIVESTATE_LID_OPEN;
339 set_event(PSXINT_CDRLID, 0x800);
343 case DRIVESTATE_LID_OPEN:
344 if (cdra_getStatus(&cdr_stat) != 0)
345 cdr_stat.Status &= ~STATUS_SHELLOPEN;
348 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
349 int was_reading = cdr.Reading;
351 SetPlaySeekRead(cdr.StatP, 0);
352 cdr.StatP |= STATUS_SHELLOPEN;
353 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
355 // IIRC this sometimes doesn't happen on real hw
356 // (when lots of commands are sent?)
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;
366 setIrq(DiskError, 0x1006);
368 set_event(PSXINT_CDRLID, cdReadTime * 30);
371 else if (cdr.StatP & STATUS_ROTATING) {
372 cdr.StatP &= ~STATUS_ROTATING;
374 else if (!(cdr_stat.Status & STATUS_SHELLOPEN)) {
378 // cdr.StatP STATUS_SHELLOPEN is "sticky"
379 // and is only cleared by CdlNop
381 cdr.DriveState = DRIVESTATE_RESCAN_CD;
382 set_event(PSXINT_CDRLID, cdReadTime * 105);
387 set_event(PSXINT_CDRLID, cdReadTime * 3);
390 case DRIVESTATE_RESCAN_CD:
391 cdr.StatP |= STATUS_ROTATING;
392 cdr.DriveState = DRIVESTATE_PREPARE_CD;
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);
399 case DRIVESTATE_PREPARE_CD:
400 if (cdr.StatP & STATUS_SEEK) {
401 SetPlaySeekRead(cdr.StatP, 0);
402 cdr.DriveState = DRIVESTATE_STANDBY;
405 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
406 set_event(PSXINT_CDRLID, cdReadTime * 26);
412 static void Find_CurTrack(const u8 *time)
416 current = msf2sec(time);
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)
426 static void generate_subq(const u8 *time)
428 unsigned char start[3], next[3];
429 int this_s, start_s, next_s, pregap;
432 if (cdr.CurTrack <= cdr.ResultTN[1])
433 cdra_getTD(cdr.CurTrack, start);
435 memcpy(start, cdr.SetSectorEnd, 3);
436 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
438 cdra_getTD(cdr.CurTrack + 1, next);
441 // last track - cd size
443 memcpy(next, cdr.SetSectorEnd, 3);
446 this_s = msf2sec(time);
447 start_s = msf2sec(start);
448 next_s = msf2sec(next);
450 cdr.TrackChanged = FALSE;
452 if (next_s - this_s < pregap && cdr.CurTrack <= cdr.ResultTN[1]) {
453 cdr.TrackChanged = TRUE;
460 relative_s = this_s - start_s;
461 if (relative_s < 0) {
463 relative_s = -relative_s;
465 lba2msf(relative_s, &cdr.subq.Relative[0],
466 &cdr.subq.Relative[1], &cdr.subq.Relative[2]);
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]);
479 static int ReadTrack(const u8 *time)
483 CDR_LOG("ReadTrack *** %02d:%02d:%02d\n", time[0], time[1], time[2]);
485 if (memcmp(cdr.Prev, time, 3) == 0)
488 ret = cdra_readTrack(time);
490 memcpy(cdr.Prev, time, 3);
492 log_unhandled("ReadTrack %02d:%02d:%02d ret %d\n",
493 time[0], time[1], time[2], ret);
497 static void UpdateSubq(const u8 *time)
499 int ret = -1, s = MSF2SECT(time[0], time[1], time[2]);
506 if (cdr.CurTrack == 1)
507 ret = cdra_readSub(time, &subq);
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);
517 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
518 time[0], time[1], time[2]);
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]);
531 static void cdrPlayInterrupt_Autopause()
534 boolean abs_lev_chselect;
537 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
538 CDR_LOG_I("autopause\n");
541 cdr.Result[0] = cdr.StatP;
542 setIrq(DataEnd, 0x1000); // 0x1000 just for logging purposes
545 SetPlaySeekRead(cdr.StatP, 0);
546 cdr.DriveState = DRIVESTATE_PAUSED;
548 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
549 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
552 cdr.Result[0] = cdr.StatP;
553 cdr.Result[1] = cdr.subq.Track;
554 cdr.Result[2] = cdr.subq.Index;
556 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
558 /* 8 is a hack. For accuracy, it should be 588. */
559 for (i = 0; i < 8; i++)
561 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
563 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
564 abs_lev_max |= abs_lev_chselect << 15;
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];
572 cdr.Result[3] = cdr.subq.Absolute[0];
573 cdr.Result[4] = cdr.subq.Absolute[1];
574 cdr.Result[5] = cdr.subq.Absolute[2];
576 cdr.Result[6] = abs_lev_max >> 0;
577 cdr.Result[7] = abs_lev_max >> 8;
579 setIrq(DataReady, 0x1001);
586 static boolean canDoTurbo(void)
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;
594 static int cdrSeekTime(unsigned char *target)
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);
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);
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;
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);
621 static u32 cdrAlignTimingHack(u32 cycles)
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.
635 * Note: always enforcing this breaks other games like Crash PAL version
636 * (inputs get dropped because bios handler doesn't see interrupts).
638 u32 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
639 while ((s32)(vint_rel - cycles) < 0)
640 vint_rel += PSXCLK / 60;
644 static void cdrUpdateTransferBuf(const u8 *buf);
645 static void cdrReadInterrupt(void);
646 static void cdrPrepCdda(s16 *buf, int samples);
648 static void msfiAdd(u8 *msfi, u32 count)
662 static void msfiSub(u8 *msfi, u32 count)
666 if ((s8)msfi[2] < 0) {
669 if ((s8)msfi[1] < 0) {
676 static int msfiEq(const u8 *a, const u8 *b)
678 return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
681 void cdrPlayReadInterrupt(void)
683 // this works but causes instability for timing sensitive games
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++;
693 cdr.LastReadSeekCycles = psxRegs.cycle;
700 if (!cdr.Play) return;
702 CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
703 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
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");
710 SetPlaySeekRead(cdr.StatP, 0);
711 cdr.TrackChanged = TRUE;
712 cdr.DriveState = DRIVESTATE_PAUSED;
715 cdra_readCDDA(cdr.SetSectorPlay, read_buf);
718 if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
719 cdrPlayInterrupt_Autopause();
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);
726 msfiAdd(cdr.SetSectorPlay, 1);
727 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], 1);
729 // update for CdlGetlocP/autopause
730 generate_subq(cdr.SetSectorPlay);
732 CDRPLAYREAD_INT(cdReadTime, 0);
734 // stop propagation since it breaks streaming
735 cdr.PhysCdPropagations = 0xff;
738 static void softReset(void)
740 cdra_getStatus(&cdr_stat);
741 if (cdr_stat.Status & STATUS_SHELLOPEN) {
742 cdr.DriveState = DRIVESTATE_LID_OPEN;
743 cdr.StatP = STATUS_SHELLOPEN;
745 else if (CdromId[0] == '\0') {
746 cdr.DriveState = DRIVESTATE_STOPPED;
750 cdr.DriveState = DRIVESTATE_STANDBY;
751 cdr.StatP = STATUS_ROTATING;
754 cdr.FifoOffset = DATA_SIZE; // fifo empty
755 cdr.LocL[0] = LOCL_INVALID;
756 cdr.Mode = MODE_SIZE_2340;
758 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
759 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
762 #define CMD_PART2 0x100
763 #define CMD_WHILE_NOT_READY 0x200
765 void cdrInterrupt(void) {
766 int start_rotating = 0;
768 u32 cycles, seekTime = 0;
769 u32 second_resp_time = 0;
774 u8 IrqStat = Acknowledge;
780 CDR_LOG_I("cmd %02x with irqstat %x\n",
781 cdr.CmdInProgress, cdr.IrqStat);
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);
791 cdr.Result[0] = cdr.Irq1Pending;
793 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
799 cdr.Result[0] = cdr.StatP;
801 Cmd = cdr.CmdInProgress;
802 cdr.CmdInProgress = 0;
811 switch (cdr.DriveState) {
812 case DRIVESTATE_PREPARE_CD:
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);
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;
829 switch (Cmd | not_ready) {
831 case CdlNop + CMD_WHILE_NOT_READY:
832 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
833 cdr.StatP &= ~STATUS_SHELLOPEN;
837 case CdlSetloc + CMD_WHILE_NOT_READY: // apparently?
838 if (cdr.StatP & STATUS_SHELLOPEN)
839 // wrong? Driver2 vs Amerzone
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))
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)
848 error = ERROR_BAD_ARGNUM;
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)
860 cdr.RetryDetected = 0;
861 memcpy(cdr.SetSector, set_loc, 3);
862 cdr.SetSector[3] = 0;
863 cdr.SetlocPending = 1;
864 cdr.errorRetryhack = 0;
872 cdr.FastBackward = 0;
876 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
878 if (ParamC != 0 && cdr.Param[0] != 0) {
879 int track = btoi( cdr.Param[0] );
881 if (track <= cdr.ResultTN[1])
882 cdr.CurTrack = track;
884 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
886 if (cdra_getTD(cdr.CurTrack, cdr.ResultTD) != -1) {
887 seekTime = cdrSeekTime(cdr.ResultTD);
888 memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
891 else if (cdr.SetlocPending) {
892 seekTime = cdrSeekTime(cdr.SetSector);
893 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
896 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
897 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
899 cdr.SetlocPending = 0;
902 Rayman: detect track changes
905 Twisted Metal 2: skip PREGAP + starting accurate SubQ
906 - plays tracks without retry play
908 Wild 9: skip PREGAP + starting accurate SubQ
909 - plays tracks without retry play
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;
918 cdr.ReportDelay = 60;
921 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
923 // BIOS player - set flag again
925 cdr.DriveState = DRIVESTATE_PLAY_READ;
926 cdr.PhysCdPropagations = 0;
928 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
933 // TODO: error 80 if stopped
936 // GameShark CD Player: Calls 2x + Play 2x
938 cdr.FastBackward = 0;
944 // GameShark CD Player: Calls 2x + Play 2x
945 cdr.FastBackward = 1;
950 if (cdr.DriveState != DRIVESTATE_STOPPED) {
951 error = ERROR_BAD_ARGNUM;
954 second_resp_time = cdReadTime * 125 / 2;
958 case CdlStandby + CMD_PART2:
964 // grab time for current track
965 cdra_getTD(cdr.CurTrack, cdr.ResultTD);
966 memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
971 SetPlaySeekRead(cdr.StatP, 0);
972 cdr.StatP &= ~STATUS_ROTATING;
973 cdr.LocL[0] = LOCL_INVALID;
975 second_resp_time = 0x800;
976 if (cdr.DriveState != DRIVESTATE_STOPPED)
977 second_resp_time = cdReadTime * 30 / 2;
979 cdr.DriveState = DRIVESTATE_STOPPED;
982 case CdlStop + CMD_PART2:
987 if (cdr.AdpcmActive) {
990 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 1); // flush adpcm
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));
1001 Gundam Battle Assault 2
1003 InuYasha - Feudal Fairy Tale
1004 Dance Dance Revolution Konamix
1005 Digimon Rumble Arena
1008 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
1010 second_resp_time = 7000;
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;
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;
1031 case CdlPause + CMD_PART2:
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
1042 second_resp_time = not_ready ? 70000 : 4100000;
1046 case CdlReset + CMD_PART2:
1047 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
1053 SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
1058 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1059 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1063 cdr.FilterFile = cdr.Param[0];
1064 cdr.FilterChannel = cdr.Param[1];
1065 cdr.FileChannelSelected = 0;
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];
1076 case CdlGetparam + CMD_WHILE_NOT_READY:
1077 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1079 cdr.Result[1] = cdr.Mode;
1081 cdr.Result[3] = cdr.FilterFile;
1082 cdr.Result[4] = cdr.FilterChannel;
1086 if (cdr.LocL[0] == LOCL_INVALID) {
1091 memcpy(cdr.Result, cdr.LocL, 8);
1096 memcpy(&cdr.Result, &cdr.subq, 8);
1099 case CdlReadT: // SetSession?
1101 second_resp_time = cdReadTime * 290 / 4;
1105 case CdlReadT + CMD_PART2:
1110 if (cdra_getTN(cdr.ResultTN) != 0) {
1114 cdr.Result[1] = itob(cdr.ResultTN[0]);
1115 cdr.Result[2] = itob(cdr.ResultTN[1]);
1119 cdr.Track = btoi(cdr.Param[0]);
1120 if (cdra_getTD(cdr.Track, cdr.ResultTD) != 0) {
1121 error = ERROR_BAD_ARGVAL;
1125 cdr.Result[1] = itob(cdr.ResultTD[0]);
1126 cdr.Result[2] = itob(cdr.ResultTD[1]);
1128 //cdr.Result[3] = itob(cdr.ResultTD[2]);
1135 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
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);
1144 Crusaders of Might and Magic = 0.5x-4x
1145 - fix cutscene speech start
1147 Eggs of Steel = 2x-?
1151 - fix cutscene speech
1156 second_resp_time = cdReadTime + seekTime;
1160 case CdlSeekL + CMD_PART2:
1161 case CdlSeekP + CMD_PART2:
1162 SetPlaySeekRead(cdr.StatP, 0);
1163 cdr.Result[0] = cdr.StatP;
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;
1177 case CdlTest + CMD_WHILE_NOT_READY:
1178 switch (cdr.Param[0]) {
1179 case 0x20: // System Controller ROM Version
1181 memcpy(cdr.Result, Test20, 4);
1185 memcpy(cdr.Result, Test22, 4);
1187 case 0x23: case 0x24:
1189 memcpy(cdr.Result, Test23, 4);
1195 second_resp_time = 20480;
1198 case CdlID + CMD_PART2:
1200 cdr.Result[0] = cdr.StatP;
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;
1211 if (cdr_stat.Type == CDRT_CDDA)
1212 cdr.Result[1] |= 0x10;
1213 if (CdromId[0] == '\0')
1214 cdr.Result[1] |= 0x80;
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]);
1220 /* 4-char string in Playstation bios boot screen */
1221 if (Config.SlowBoot == 1)
1222 memcpy(&cdr.Result[4], "PCSX", 4);
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';
1232 cdr.Result[7] = 'A';
1238 case CdlInit + CMD_WHILE_NOT_READY:
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);
1250 case CdlGetQ + CMD_WHILE_NOT_READY:
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;
1267 case CdlReadToc + CMD_PART2:
1268 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1274 if (cdr.Reading && !cdr.SetlocPending)
1277 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1279 if (cdr_stat.Type != CDRT_DATA && !(cdr.Mode & MODE_CDDA)) {
1280 error = ERROR_INVALIDCMD;
1285 if (cdr.SetlocPending) {
1286 seekTime = cdrSeekTime(cdr.SetSector);
1287 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1288 cdr.SetlocPending = 0;
1291 cdr.FileChannelSelected = 0;
1292 cdr.AdpcmActive = 0;
1294 // Fighting Force 2 - update subq time immediately
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);
1305 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1307 if (Config.hacks.cdr_read_timing)
1308 cycles = cdrAlignTimingHack(cycles);
1309 else if (canDoTurbo())
1310 cycles = cdReadTime / 2;
1311 CDRPLAYREAD_INT(cycles, 1);
1313 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1319 error = ERROR_INVALIDCMD;
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]);
1331 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1332 cdr.DriveState = DRIVESTATE_STANDBY;
1333 cdr.StatP |= STATUS_ROTATING;
1336 if (second_resp_time) {
1337 cdr.CmdInProgress = Cmd | 0x100;
1338 set_event(PSXINT_CDR, second_resp_time);
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);
1345 setIrq(IrqStat, Cmd);
1348 static void cdrPrepCdda(s16 *buf, int samples)
1350 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
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]);
1359 static void cdrReadInterruptSetResult(unsigned char result)
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;
1373 cdr.Result[0] = result;
1374 setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1377 static void cdrUpdateTransferBuf(const u8 *buf)
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);
1389 static void cdrReadInterrupt(void)
1391 const struct { u8 file, chan, mode, coding; } *subhdr;
1392 const u8 *buf = NULL;
1393 int deliver_data = 1;
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);
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;
1412 read_ok = ReadTrack(cdr.SetSectorPlay);
1414 buf = cdra_getBuffer();
1419 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1420 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1421 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1424 memcpy(cdr.LocL, buf, 8);
1426 if (!cdr.IrqStat && !cdr.Irq1Pending)
1427 cdrUpdateTransferBuf(buf);
1429 subhdr = (void *)(buf + 4);
1431 // try to process as adpcm
1432 if (!(cdr.Mode & MODE_STRSND))
1434 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
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))
1440 if (subhdr->chan & 0x80) { // ?
1441 if (subhdr->chan != 0xff)
1442 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1445 if (!cdr.FileChannelSelected) {
1446 cdr.CurFile = subhdr->file;
1447 cdr.CurChannel = subhdr->chan;
1448 cdr.FileChannelSelected = 1;
1450 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1453 // accepted as adpcm
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);
1464 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
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]);
1474 Croc 2: $40 - only FORM1 (*)
1475 Judge Dredd: $C8 - only FORM1 (*)
1476 Sim Theme Park - no adpcm at all (zero)
1480 cdrReadInterruptSetResult(cdr.StatP);
1482 msfiAdd(cdr.SetSectorPlay, 1);
1483 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], 0);
1485 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
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
1499 unsigned char cdrRead0(void) {
1501 cdr.Ctrl |= cdr.AdpcmActive << 2;
1502 cdr.Ctrl |= cdr.ResultReady << 5;
1503 cdr.Ctrl |= ((signed int)(cdr.FifoOffset - cdr.FifoSize) >> 31) & 0x40;
1507 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1509 return psxHu8(0x1800) = cdr.Ctrl;
1512 void cdrWrite0(unsigned char rt) {
1513 CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
1515 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1518 unsigned char cdrRead1(void) {
1519 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1520 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1524 if (cdr.ResultP == cdr.ResultC)
1525 cdr.ResultReady = 0;
1527 CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1529 return psxHu8(0x1801);
1532 void 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);
1536 switch (cdr.Ctrl & 3) {
1540 cdr.AttenuatorRightToRightT = rt;
1547 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1550 SysPrintf(" Param[%d] = {", cdr.ParamC);
1551 for (i = 0; i < cdr.ParamC; i++)
1552 SysPrintf(" %x,", cdr.Param[i]);
1555 SysPrintf(" @%08x\n", psxRegs.pc);
1558 cdr.ResultReady = 0;
1561 if (!cdr.CmdInProgress) {
1562 cdr.CmdInProgress = rt;
1563 // should be something like 12k + controller delays
1564 set_event(PSXINT_CDR, 5000);
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;
1576 unsigned char cdrRead2(void) {
1577 unsigned char ret = cdr.Transfer[0x920];
1579 if (cdr.FifoOffset < cdr.FifoSize)
1580 ret = cdr.Transfer[cdr.FifoOffset++];
1582 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1584 CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
1588 void 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);
1592 switch (cdr.Ctrl & 3) {
1594 if (cdr.ParamC < 8) // FIXME: size and wrapping
1595 cdr.Param[cdr.ParamC++] = rt;
1599 setIrq(cdr.IrqStat, 0x1005);
1602 cdr.AttenuatorLeftToLeftT = rt;
1605 cdr.AttenuatorRightToLeftT = rt;
1610 unsigned char cdrRead3(void) {
1612 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1614 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
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);
1621 void cdrWrite3(unsigned char rt) {
1622 const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
1624 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1626 switch (cdr.Ctrl & 3) {
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);
1639 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1640 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1643 if (cdr.CmdInProgress) {
1644 c = 2048 - (psxRegs.cycle - nextCycle);
1645 c = MAX_VALUE(c, 512);
1647 set_event(PSXINT_CDR, c);
1656 cdr.AttenuatorLeftToRightT = rt;
1660 log_unhandled("Mute ADPCM?\n");
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)
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);
1678 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1679 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1681 else if (rt & 0x80) {
1682 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1683 case MODE_SIZE_2328:
1685 cdr.FifoOffset = 12;
1686 cdr.FifoSize = 2048 + 12;
1689 case MODE_SIZE_2340:
1692 cdr.FifoSize = 2340;
1696 else if (!(rt & 0xc0))
1697 cdr.FifoOffset = DATA_SIZE; // fifo empty
1700 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1701 u32 cdsize, max_words, cycles;
1706 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1707 if (cdr.FifoOffset == 0) {
1709 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1714 switch (chcr & 0x71000000) {
1717 ptr = getDmaRam(madr, &max_words);
1718 if (ptr == INVALID_PTR) {
1719 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1723 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1726 GS CDX: Enhancement CD crash
1729 - Spams DMA3 and gets buffer overrun
1731 size = DATA_SIZE - cdr.FifoOffset;
1734 if (size > max_words * 4)
1735 size = max_words * 4;
1738 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1739 cdr.FifoOffset += size;
1741 if (size < cdsize) {
1742 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1743 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1745 psxCpu->Clear(madr, cdsize / 4);
1747 cycles = (cdsize / 4) * 24;
1748 set_event(PSXINT_CDRDMA, cycles);
1750 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1752 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1753 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1757 psxRegs.cycle += cycles - 20;
1759 if (canDoTurbo() && cdr.Reading && cdr.FifoOffset >= 2048)
1760 CDRPLAYREAD_INT(cycles + 4096, 1);
1764 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1768 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1772 void cdrDmaInterrupt(void)
1774 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1776 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1781 static void getCdInfo(void)
1783 cdra_getTN(cdr.ResultTN);
1784 cdra_getTD(0, cdr.SetSectorEnd);
1785 cdra_getStatus(&cdr_stat);
1789 memset(&cdr, 0, sizeof(cdr));
1792 cdr.FilterChannel = 0;
1794 cdr.IrqStat = NoIntr;
1796 // BIOS player - default values
1797 cdr.AttenuatorLeftToLeft = 0x80;
1798 cdr.AttenuatorLeftToRight = 0x00;
1799 cdr.AttenuatorRightToLeft = 0x00;
1800 cdr.AttenuatorRightToRight = 0x80;
1806 int cdrFreeze(void *f, int Mode) {
1810 cdr.freeze_ver = 0x63647203;
1811 gzfreeze(&cdr, sizeof(cdr));
1814 cdr.ParamP = cdr.ParamC;
1815 tmp = cdr.FifoOffset;
1818 gzfreeze(&tmp, sizeof(tmp));
1821 u8 ll = 0, lr = 0, rl = 0, rr = 0;
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;
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]);
1837 if (tmpp[0] != 0xff)
1841 if (cdr.freeze_ver < 0x63647202)
1842 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1844 Find_CurTrack(cdr.SetSectorPlay);
1847 ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToRight,
1848 rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1849 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1855 void LidInterrupt(void) {
1857 cdrLidSeekInterrupt();