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.
29 #include "psxevents.h"
30 #include "arm_features.h"
34 #define CDR_LOG SysPrintf
39 #define CDR_LOG_I SysPrintf
41 #define CDR_LOG_I(fmt, ...) \
42 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
45 #define CDR_LOG_IO SysPrintf
47 #define CDR_LOG_IO(...)
49 //#define CDR_LOG_CMD_IRQ
52 // unused members maintain savesate compatibility
53 unsigned char unused0;
54 unsigned char unused1;
55 unsigned char IrqMask;
56 unsigned char unused2;
58 unsigned char IrqStat;
62 unsigned char Transfer[DATA_SIZE];
66 unsigned char Relative[3];
67 unsigned char Absolute[3];
69 unsigned char TrackChanged;
70 unsigned char ReportDelay;
71 unsigned char PhysCdPropagations;
72 unsigned short sectorsRead;
73 unsigned int freeze_ver;
75 unsigned char Prev[4];
76 unsigned char Param[8];
77 unsigned char Result[16];
81 unsigned char ResultC;
82 unsigned char ResultP;
83 unsigned char ResultReady;
85 unsigned char SubqForwardSectors;
86 unsigned char SetlocPending;
89 unsigned char ResultTN[6];
90 unsigned char ResultTD[4];
91 unsigned char SetSectorPlay[4];
92 unsigned char SetSectorEnd[4];
93 unsigned char SetSector[4];
98 unsigned char FileChannelSelected;
99 unsigned char CurFile, CurChannel;
100 int FilterFile, FilterChannel;
101 unsigned char LocL[8];
112 u32 LastReadSeekCycles;
116 u8 DriveState; // enum drive_state
121 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
122 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
123 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
124 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
126 static s16 read_buf[CD_FRAMESIZE_RAW/2];
128 /* CD-ROM magic numbers */
129 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
134 #define CdlBackward 5
142 #define CdlSetfilter 13
143 #define CdlSetmode 14
144 #define CdlGetparam 15
145 #define CdlGetlocL 16
146 #define CdlGetlocP 17
152 #define CdlSetclock 23
153 #define CdlGetclock 24
159 #define CdlReadToc 30
161 #ifdef CDR_LOG_CMD_IRQ
162 static const char * const CmdName[0x100] = {
163 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
164 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
165 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
166 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
167 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
168 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
169 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
170 "CdlInit", NULL, "CDlReadToc", NULL
174 unsigned char Test04[] = { 0 };
175 unsigned char Test05[] = { 0 };
176 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
177 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
178 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
184 #define Acknowledge 3
189 #define MODE_SPEED (1<<7) // 0x80
190 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
191 #define MODE_SIZE_2340 (1<<5) // 0x20
192 #define MODE_SIZE_2328 (1<<4) // 0x10
193 #define MODE_SIZE_2048 (0<<4) // 0x00
194 #define MODE_SF (1<<3) // 0x08 channel on/off
195 #define MODE_REPORT (1<<2) // 0x04
196 #define MODE_AUTOPAUSE (1<<1) // 0x02
197 #define MODE_CDDA (1<<0) // 0x01
200 #define STATUS_PLAY (1<<7) // 0x80
201 #define STATUS_SEEK (1<<6) // 0x40
202 #define STATUS_READ (1<<5) // 0x20
203 #define STATUS_SHELLOPEN (1<<4) // 0x10
204 #define STATUS_UNKNOWN3 (1<<3) // 0x08
205 #define STATUS_SEEKERROR (1<<2) // 0x04
206 #define STATUS_ROTATING (1<<1) // 0x02
207 #define STATUS_ERROR (1<<0) // 0x01
210 #define ERROR_NOTREADY (1<<7) // 0x80
211 #define ERROR_INVALIDCMD (1<<6) // 0x40
212 #define ERROR_BAD_ARGNUM (1<<5) // 0x20
213 #define ERROR_BAD_ARGVAL (1<<4) // 0x10
214 #define ERROR_SHELLOPEN (1<<3) // 0x08
216 // 1x = 75 sectors per second
217 // PSXCLK = 1 sec in the ps
218 // so (PSXCLK / 75) = cdr read time (linuzappz)
219 #define cdReadTime (PSXCLK / 75)
221 #define LOCL_INVALID 0xff
222 #define SUBQ_FORWARD_SECTORS 2u
225 DRIVESTATE_STANDBY = 0, // different from paused
227 DRIVESTATE_RESCAN_CD,
228 DRIVESTATE_PREPARE_CD,
231 DRIVESTATE_PLAY_READ,
235 static struct CdrStat stat;
237 static unsigned int msf2sec(const u8 *msf) {
238 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
241 // for that weird psemu API..
242 static unsigned int fsm2sec(const u8 *msf) {
243 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
246 static void sec2msf(unsigned int s, u8 *msf) {
247 msf[0] = s / 75 / 60;
248 s = s - msf[0] * 75 * 60;
254 // cdrPlayReadInterrupt
255 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
257 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
259 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
261 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
262 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
263 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
266 #define StopReading() { \
268 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
271 #define StopCdda() { \
272 if (cdr.Play && !Config.Cdda) CDR_stop(); \
274 cdr.FastForward = 0; \
275 cdr.FastBackward = 0; \
278 #define SetPlaySeekRead(x, f) { \
279 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
283 #define SetResultSize_(size) { \
285 cdr.ResultC = size; \
286 cdr.ResultReady = 1; \
289 #define SetResultSize(size) { \
290 if (cdr.ResultP < cdr.ResultC) \
291 CDR_LOG_I("overwriting result, len=%u\n", cdr.ResultC); \
292 SetResultSize_(size); \
295 static void setIrq(u8 irq, int log_cmd)
297 u8 old = cdr.IrqStat & cdr.IrqMask ? 1 : 0;
298 u8 new_ = irq & cdr.IrqMask ? 1 : 0;
301 if ((old ^ new_) & new_)
302 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
304 #ifdef CDR_LOG_CMD_IRQ
308 CDR_LOG_I("CDR IRQ=%d cmd %02x irqstat %02x: ",
309 !!(cdr.IrqStat & cdr.IrqMask), log_cmd, cdr.IrqStat);
310 for (i = 0; i < cdr.ResultC; i++)
311 SysPrintf("%02x ", cdr.Result[i]);
317 // timing used in this function was taken from tests on real hardware
318 // (yes it's slow, but you probably don't want to modify it)
319 void cdrLidSeekInterrupt(void)
321 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
323 switch (cdr.DriveState) {
325 case DRIVESTATE_STANDBY:
328 SetPlaySeekRead(cdr.StatP, 0);
330 if (CDR_getStatus(&stat) == -1)
333 if (stat.Status & STATUS_SHELLOPEN)
335 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
336 cdr.DriveState = DRIVESTATE_LID_OPEN;
337 set_event(PSXINT_CDRLID, 0x800);
341 case DRIVESTATE_LID_OPEN:
342 if (CDR_getStatus(&stat) == -1)
343 stat.Status &= ~STATUS_SHELLOPEN;
346 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
348 SetPlaySeekRead(cdr.StatP, 0);
349 cdr.StatP |= STATUS_SHELLOPEN;
351 // IIRC this sometimes doesn't happen on real hw
352 // (when lots of commands are sent?)
354 cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
355 cdr.Result[1] = ERROR_SHELLOPEN;
356 if (cdr.CmdInProgress) {
357 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
358 cdr.CmdInProgress = 0;
359 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
360 cdr.Result[1] = ERROR_NOTREADY;
362 setIrq(DiskError, 0x1006);
364 set_event(PSXINT_CDRLID, cdReadTime * 30);
367 else if (cdr.StatP & STATUS_ROTATING) {
368 cdr.StatP &= ~STATUS_ROTATING;
370 else if (!(stat.Status & STATUS_SHELLOPEN)) {
374 // cdr.StatP STATUS_SHELLOPEN is "sticky"
375 // and is only cleared by CdlNop
377 cdr.DriveState = DRIVESTATE_RESCAN_CD;
378 set_event(PSXINT_CDRLID, cdReadTime * 105);
383 set_event(PSXINT_CDRLID, cdReadTime * 3);
386 case DRIVESTATE_RESCAN_CD:
387 cdr.StatP |= STATUS_ROTATING;
388 cdr.DriveState = DRIVESTATE_PREPARE_CD;
390 // this is very long on real hardware, over 6 seconds
391 // make it a bit faster here...
392 set_event(PSXINT_CDRLID, cdReadTime * 150);
395 case DRIVESTATE_PREPARE_CD:
396 if (cdr.StatP & STATUS_SEEK) {
397 SetPlaySeekRead(cdr.StatP, 0);
398 cdr.DriveState = DRIVESTATE_STANDBY;
401 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
402 set_event(PSXINT_CDRLID, cdReadTime * 26);
408 static void Find_CurTrack(const u8 *time)
412 current = msf2sec(time);
414 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
415 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
416 sect = fsm2sec(cdr.ResultTD);
417 if (sect - current >= 150)
422 static void generate_subq(const u8 *time)
424 unsigned char start[3], next[3];
425 unsigned int this_s, start_s, next_s, pregap;
428 CDR_getTD(cdr.CurTrack, start);
429 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
431 CDR_getTD(cdr.CurTrack + 1, next);
434 // last track - cd size
436 next[0] = cdr.SetSectorEnd[2];
437 next[1] = cdr.SetSectorEnd[1];
438 next[2] = cdr.SetSectorEnd[0];
441 this_s = msf2sec(time);
442 start_s = fsm2sec(start);
443 next_s = fsm2sec(next);
445 cdr.TrackChanged = FALSE;
447 if (next_s - this_s < pregap) {
448 cdr.TrackChanged = TRUE;
455 relative_s = this_s - start_s;
456 if (relative_s < 0) {
458 relative_s = -relative_s;
460 sec2msf(relative_s, cdr.subq.Relative);
462 cdr.subq.Track = itob(cdr.CurTrack);
463 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
464 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
465 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
466 cdr.subq.Absolute[0] = itob(time[0]);
467 cdr.subq.Absolute[1] = itob(time[1]);
468 cdr.subq.Absolute[2] = itob(time[2]);
471 static int ReadTrack(const u8 *time)
473 unsigned char tmp[3];
476 tmp[0] = itob(time[0]);
477 tmp[1] = itob(time[1]);
478 tmp[2] = itob(time[2]);
480 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
482 if (memcmp(cdr.Prev, tmp, 3) == 0)
485 read_ok = CDR_readTrack(tmp);
487 memcpy(cdr.Prev, tmp, 3);
491 static void UpdateSubq(const u8 *time)
493 const struct SubQ *subq;
494 int s = MSF2SECT(time[0], time[1], time[2]);
500 subq = (struct SubQ *)CDR_getBufferSub(s);
501 if (subq != NULL && cdr.CurTrack == 1) {
502 crc = calcCrc((u8 *)subq + 12, 10);
503 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
504 cdr.subq.Track = subq->TrackNumber;
505 cdr.subq.Index = subq->IndexNumber;
506 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
507 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
510 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
511 time[0], time[1], time[2]);
518 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
519 cdr.subq.Track, cdr.subq.Index,
520 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
521 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
524 static void cdrPlayInterrupt_Autopause()
527 boolean abs_lev_chselect;
530 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
531 CDR_LOG_I("autopause\n");
534 cdr.Result[0] = cdr.StatP;
535 setIrq(DataEnd, 0x1000); // 0x1000 just for logging purposes
538 SetPlaySeekRead(cdr.StatP, 0);
539 cdr.DriveState = DRIVESTATE_PAUSED;
541 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
542 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
545 cdr.Result[0] = cdr.StatP;
546 cdr.Result[1] = cdr.subq.Track;
547 cdr.Result[2] = cdr.subq.Index;
549 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
551 /* 8 is a hack. For accuracy, it should be 588. */
552 for (i = 0; i < 8; i++)
554 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
556 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
557 abs_lev_max |= abs_lev_chselect << 15;
559 if (cdr.subq.Absolute[2] & 0x10) {
560 cdr.Result[3] = cdr.subq.Relative[0];
561 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
562 cdr.Result[5] = cdr.subq.Relative[2];
565 cdr.Result[3] = cdr.subq.Absolute[0];
566 cdr.Result[4] = cdr.subq.Absolute[1];
567 cdr.Result[5] = cdr.subq.Absolute[2];
569 cdr.Result[6] = abs_lev_max >> 0;
570 cdr.Result[7] = abs_lev_max >> 8;
572 setIrq(DataReady, 0x1001);
579 static int cdrSeekTime(unsigned char *target)
581 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
582 int seekTime = abs(diff) * (cdReadTime / 2000);
583 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
584 seekTime = MAX_VALUE(seekTime, 20000);
586 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
587 // and then wants some slack time
588 if (cdr.DriveState == DRIVESTATE_PAUSED || cyclesSinceRS < cdReadTime *3/2)
589 seekTime += cdReadTime;
591 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
592 CDR_LOG("seek: %.2f %.2f (%.2f) st %d di %d\n", (float)seekTime / PSXCLK,
593 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime,
594 cdr.DriveState, diff);
598 static u32 cdrAlignTimingHack(u32 cycles)
601 * timing hack for T'ai Fu - Wrath of the Tiger:
602 * The game has a bug where it issues some cdc commands from a low priority
603 * vint handler, however there is a higher priority default bios handler
604 * that acks the vint irq and returns, so game's handler is not reached
605 * (see bios irq handler chains at e004 and the game's irq handling func
606 * at 80036810). For the game to work, vint has to arrive after the bios
607 * vint handler rejects some other irq (of which only cd and rcnt2 are
608 * active), but before the game's handler loop reads I_STAT. The time
609 * window for this is quite small (~1k cycles of so). Apparently this
610 * somehow happens naturally on the real hardware.
612 * Note: always enforcing this breaks other games like Crash PAL version
613 * (inputs get dropped because bios handler doesn't see interrupts).
616 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
618 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
619 vint_rel += PSXCLK / 60;
620 while ((s32)(vint_rel - cycles) < 0)
621 vint_rel += PSXCLK / 60;
625 static void cdrUpdateTransferBuf(const u8 *buf);
626 static void cdrReadInterrupt(void);
627 static void cdrPrepCdda(s16 *buf, int samples);
629 static void msfiAdd(u8 *msfi, u32 count)
643 static void msfiSub(u8 *msfi, u32 count)
647 if ((s8)msfi[2] < 0) {
650 if ((s8)msfi[1] < 0) {
657 void cdrPlayReadInterrupt(void)
659 int hit = CDR_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
660 if (!hit && cdr.PhysCdPropagations++ < 222) {
661 // this propagates real cdrom delays to the emulated game
662 CDRPLAYREAD_INT(cdReadTime / 2, 0);
665 cdr.PhysCdPropagations = 0;
667 cdr.LastReadSeekCycles = psxRegs.cycle;
674 if (!cdr.Play) return;
676 CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
677 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
679 cdr.DriveState = DRIVESTATE_PLAY_READ;
680 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
681 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
682 CDR_LOG_I("end stop\n");
684 SetPlaySeekRead(cdr.StatP, 0);
685 cdr.TrackChanged = TRUE;
686 cdr.DriveState = DRIVESTATE_PAUSED;
689 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
692 if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
693 cdrPlayInterrupt_Autopause();
695 if (cdr.Play && !Config.Cdda) {
696 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
697 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
700 msfiAdd(cdr.SetSectorPlay, 1);
701 CDR_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
703 // update for CdlGetlocP/autopause
704 generate_subq(cdr.SetSectorPlay);
706 CDRPLAYREAD_INT(cdReadTime, 0);
709 static void softReset(void)
711 CDR_getStatus(&stat);
712 if (stat.Status & STATUS_SHELLOPEN) {
713 cdr.DriveState = DRIVESTATE_LID_OPEN;
714 cdr.StatP = STATUS_SHELLOPEN;
716 else if (CdromId[0] == '\0') {
717 cdr.DriveState = DRIVESTATE_STOPPED;
721 cdr.DriveState = DRIVESTATE_STANDBY;
722 cdr.StatP = STATUS_ROTATING;
725 cdr.FifoOffset = DATA_SIZE; // fifo empty
726 cdr.LocL[0] = LOCL_INVALID;
727 cdr.Mode = MODE_SIZE_2340;
729 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
730 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
733 #define CMD_PART2 0x100
734 #define CMD_WHILE_NOT_READY 0x200
736 void cdrInterrupt(void) {
737 int start_rotating = 0;
739 u32 cycles, seekTime = 0;
740 u32 second_resp_time = 0;
746 u8 IrqStat = Acknowledge;
752 CDR_LOG_I("cmd %02x with irqstat %x\n",
753 cdr.CmdInProgress, cdr.IrqStat);
756 if (cdr.Irq1Pending) {
757 // hand out the "newest" sector, according to nocash
758 cdrUpdateTransferBuf(CDR_getBuffer());
759 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
760 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
761 cdr.CmdInProgress, cdr.Irq1Pending);
763 cdr.Result[0] = cdr.Irq1Pending;
765 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
771 cdr.Result[0] = cdr.StatP;
773 Cmd = cdr.CmdInProgress;
774 cdr.CmdInProgress = 0;
783 switch (cdr.DriveState) {
784 case DRIVESTATE_PREPARE_CD:
786 // Syphon filter 2 expects commands to work shortly after it sees
787 // STATUS_ROTATING, so give up trying to emulate the startup seq
788 cdr.DriveState = DRIVESTATE_STANDBY;
789 cdr.StatP &= ~STATUS_SEEK;
790 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
794 case DRIVESTATE_LID_OPEN:
795 case DRIVESTATE_RESCAN_CD:
796 // no disk or busy with the initial scan, allowed cmds are limited
797 not_ready = CMD_WHILE_NOT_READY;
801 switch (Cmd | not_ready) {
803 case CdlNop + CMD_WHILE_NOT_READY:
804 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
805 cdr.StatP &= ~STATUS_SHELLOPEN;
809 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
810 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
812 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
813 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))
815 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
816 if (++cdr.errorRetryhack > 100)
818 error = ERROR_BAD_ARGNUM;
823 for (i = 0; i < 3; i++)
824 set_loc[i] = btoi(cdr.Param[i]);
825 memcpy(cdr.SetSector, set_loc, 3);
826 cdr.SetSector[3] = 0;
827 cdr.SetlocPending = 1;
828 cdr.errorRetryhack = 0;
837 cdr.FastBackward = 0;
841 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
843 if (ParamC != 0 && cdr.Param[0] != 0) {
844 int track = btoi( cdr.Param[0] );
846 if (track <= cdr.ResultTN[1])
847 cdr.CurTrack = track;
849 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
851 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
852 for (i = 0; i < 3; i++)
853 set_loc[i] = cdr.ResultTD[2 - i];
854 seekTime = cdrSeekTime(set_loc);
855 memcpy(cdr.SetSectorPlay, set_loc, 3);
858 else if (cdr.SetlocPending) {
859 seekTime = cdrSeekTime(cdr.SetSector);
860 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
863 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
864 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
866 cdr.SetlocPending = 0;
869 Rayman: detect track changes
872 Twisted Metal 2: skip PREGAP + starting accurate SubQ
873 - plays tracks without retry play
875 Wild 9: skip PREGAP + starting accurate SubQ
876 - plays tracks without retry play
878 Find_CurTrack(cdr.SetSectorPlay);
879 generate_subq(cdr.SetSectorPlay);
880 cdr.LocL[0] = LOCL_INVALID;
881 cdr.SubqForwardSectors = 1;
882 cdr.TrackChanged = FALSE;
883 cdr.FileChannelSelected = 0;
885 cdr.ReportDelay = 60;
889 CDR_play(cdr.SetSectorPlay);
891 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
893 // BIOS player - set flag again
895 cdr.DriveState = DRIVESTATE_PLAY_READ;
897 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
902 // TODO: error 80 if stopped
905 // GameShark CD Player: Calls 2x + Play 2x
907 cdr.FastBackward = 0;
913 // GameShark CD Player: Calls 2x + Play 2x
914 cdr.FastBackward = 1;
919 if (cdr.DriveState != DRIVESTATE_STOPPED) {
920 error = ERROR_BAD_ARGNUM;
923 cdr.DriveState = DRIVESTATE_STANDBY;
924 second_resp_time = cdReadTime * 125 / 2;
928 case CdlStandby + CMD_PART2:
934 // grab time for current track
935 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
937 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
938 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
939 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
944 SetPlaySeekRead(cdr.StatP, 0);
945 cdr.StatP &= ~STATUS_ROTATING;
946 cdr.LocL[0] = LOCL_INVALID;
948 second_resp_time = 0x800;
949 if (cdr.DriveState != DRIVESTATE_STOPPED)
950 second_resp_time = cdReadTime * 30 / 2;
952 cdr.DriveState = DRIVESTATE_STOPPED;
955 case CdlStop + CMD_PART2:
960 if (cdr.AdpcmActive) {
963 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 1); // flush adpcm
968 // how the drive maintains the position while paused is quite
969 // complicated, this is the minimum to make "Bedlam" happy
970 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
974 Gundam Battle Assault 2
976 InuYasha - Feudal Fairy Tale
977 Dance Dance Revolution Konamix
980 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
982 second_resp_time = 7000;
986 second_resp_time = 2 * 1097107;
988 SetPlaySeekRead(cdr.StatP, 0);
989 DriveStateOld = cdr.DriveState;
990 cdr.DriveState = DRIVESTATE_PAUSED;
991 if (DriveStateOld == DRIVESTATE_SEEK) {
992 // According to Duckstation this fails, but the
993 // exact conditions and effects are not clear.
994 // Moto Racer World Tour seems to rely on this.
995 // For now assume pause works anyway, just errors out.
996 error = ERROR_NOTREADY;
1001 case CdlPause + CMD_PART2:
1006 case CdlReset + CMD_WHILE_NOT_READY:
1007 // note: nocash and Duckstation calls this 'Init', but
1008 // the official SDK calls it 'Reset', and so do we
1012 second_resp_time = not_ready ? 70000 : 4100000;
1016 case CdlReset + CMD_PART2:
1017 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
1023 SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
1028 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1029 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1033 cdr.FilterFile = cdr.Param[0];
1034 cdr.FilterChannel = cdr.Param[1];
1035 cdr.FileChannelSelected = 0;
1039 case CdlSetmode + CMD_WHILE_NOT_READY:
1040 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1041 cdr.Mode = cdr.Param[0];
1045 case CdlGetparam + CMD_WHILE_NOT_READY:
1046 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1048 cdr.Result[1] = cdr.Mode;
1050 cdr.Result[3] = cdr.FilterFile;
1051 cdr.Result[4] = cdr.FilterChannel;
1055 if (cdr.LocL[0] == LOCL_INVALID) {
1060 memcpy(cdr.Result, cdr.LocL, 8);
1065 memcpy(&cdr.Result, &cdr.subq, 8);
1068 case CdlReadT: // SetSession?
1070 second_resp_time = cdReadTime * 290 / 4;
1074 case CdlReadT + CMD_PART2:
1079 if (CDR_getTN(cdr.ResultTN) == -1) {
1083 cdr.Result[1] = itob(cdr.ResultTN[0]);
1084 cdr.Result[2] = itob(cdr.ResultTN[1]);
1088 cdr.Track = btoi(cdr.Param[0]);
1089 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1090 error = ERROR_BAD_ARGVAL;
1094 cdr.Result[1] = itob(cdr.ResultTD[2]);
1095 cdr.Result[2] = itob(cdr.ResultTD[1]);
1097 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1104 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1106 seekTime = cdrSeekTime(cdr.SetSector);
1107 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1108 cdr.DriveState = DRIVESTATE_SEEK;
1109 CDR_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1110 cdr.SetSectorPlay[2]);
1112 Crusaders of Might and Magic = 0.5x-4x
1113 - fix cutscene speech start
1115 Eggs of Steel = 2x-?
1119 - fix cutscene speech
1124 second_resp_time = cdReadTime + seekTime;
1128 case CdlSeekL + CMD_PART2:
1129 case CdlSeekP + CMD_PART2:
1130 SetPlaySeekRead(cdr.StatP, 0);
1131 cdr.Result[0] = cdr.StatP;
1134 Find_CurTrack(cdr.SetSectorPlay);
1135 read_ok = ReadTrack(cdr.SetSectorPlay);
1136 if (read_ok && (buf = CDR_getBuffer()))
1137 memcpy(cdr.LocL, buf, 8);
1138 UpdateSubq(cdr.SetSectorPlay);
1139 cdr.DriveState = DRIVESTATE_STANDBY;
1140 cdr.TrackChanged = FALSE;
1141 cdr.LastReadSeekCycles = psxRegs.cycle;
1145 case CdlTest + CMD_WHILE_NOT_READY:
1146 switch (cdr.Param[0]) {
1147 case 0x20: // System Controller ROM Version
1149 memcpy(cdr.Result, Test20, 4);
1153 memcpy(cdr.Result, Test22, 4);
1155 case 0x23: case 0x24:
1157 memcpy(cdr.Result, Test23, 4);
1163 second_resp_time = 20480;
1166 case CdlID + CMD_PART2:
1168 cdr.Result[0] = cdr.StatP;
1173 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1174 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1175 cdr.Result[1] = 0xc0;
1179 cdr.Result[1] |= 0x10;
1180 if (CdromId[0] == '\0')
1181 cdr.Result[1] |= 0x80;
1183 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1184 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1185 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1187 /* This adds the string "PCSX" in Playstation bios boot screen */
1188 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1193 case CdlInit + CMD_WHILE_NOT_READY:
1196 SetPlaySeekRead(cdr.StatP, 0);
1197 // yes, it really sets STATUS_SHELLOPEN
1198 cdr.StatP |= STATUS_SHELLOPEN;
1199 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1200 set_event(PSXINT_CDRLID, 20480);
1205 case CdlGetQ + CMD_WHILE_NOT_READY:
1209 case CdlReadToc + CMD_WHILE_NOT_READY:
1210 cdr.LocL[0] = LOCL_INVALID;
1211 second_resp_time = cdReadTime * 180 / 4;
1215 case CdlReadToc + CMD_PART2:
1216 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1222 if (cdr.Reading && !cdr.SetlocPending)
1225 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1227 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1228 // Read* acts as play for cdda tracks in cdda mode
1232 if (cdr.SetlocPending) {
1233 seekTime = cdrSeekTime(cdr.SetSector);
1234 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1235 cdr.SetlocPending = 0;
1238 cdr.FileChannelSelected = 0;
1239 cdr.AdpcmActive = 0;
1241 // Fighting Force 2 - update subq time immediately
1243 UpdateSubq(cdr.SetSectorPlay);
1244 cdr.LocL[0] = LOCL_INVALID;
1245 cdr.SubqForwardSectors = 1;
1246 cdr.sectorsRead = 0;
1247 cdr.DriveState = DRIVESTATE_SEEK;
1248 CDR_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1249 cdr.SetSectorPlay[2]);
1251 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1253 if (Config.hacks.cdr_read_timing)
1254 cycles = cdrAlignTimingHack(cycles);
1255 CDRPLAYREAD_INT(cycles, 1);
1257 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1263 error = ERROR_INVALIDCMD;
1268 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1269 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1270 IrqStat = DiskError;
1271 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1275 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1276 cdr.DriveState = DRIVESTATE_STANDBY;
1277 cdr.StatP |= STATUS_ROTATING;
1280 if (second_resp_time) {
1281 cdr.CmdInProgress = Cmd | 0x100;
1282 set_event(PSXINT_CDR, second_resp_time);
1284 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1285 cdr.CmdInProgress = cdr.Cmd;
1286 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1289 setIrq(IrqStat, Cmd);
1293 #define ssat32_to_16(v) \
1294 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1296 #define ssat32_to_16(v) do { \
1297 if (v < -32768) v = -32768; \
1298 else if (v > 32767) v = 32767; \
1302 static void cdrPrepCdda(s16 *buf, int samples)
1304 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1306 for (i = 0; i < samples; i++) {
1307 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1308 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1313 static void cdrReadInterruptSetResult(unsigned char result)
1316 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1317 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1318 cdr.CmdInProgress, cdr.IrqStat);
1319 cdr.Irq1Pending = result;
1323 cdr.Result[0] = result;
1324 setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1327 static void cdrUpdateTransferBuf(const u8 *buf)
1331 memcpy(cdr.Transfer, buf, DATA_SIZE);
1332 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1333 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1334 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1335 if (cdr.FifoOffset < 2048 + 12)
1336 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1339 static void cdrReadInterrupt(void)
1341 const struct { u8 file, chan, mode, coding; } *subhdr;
1342 const u8 *buf = NULL;
1343 int deliver_data = 1;
1348 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1349 msfiAdd(subqPos, cdr.SubqForwardSectors);
1350 UpdateSubq(subqPos);
1351 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1352 cdr.SubqForwardSectors++;
1353 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1357 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1358 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1359 cdr.DriveState = DRIVESTATE_PLAY_READ;
1362 read_ok = ReadTrack(cdr.SetSectorPlay);
1364 buf = CDR_getBuffer();
1369 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1370 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1371 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1374 memcpy(cdr.LocL, buf, 8);
1376 if (!cdr.IrqStat && !cdr.Irq1Pending)
1377 cdrUpdateTransferBuf(buf);
1379 subhdr = (void *)(buf + 4);
1381 // try to process as adpcm
1382 if (!(cdr.Mode & MODE_STRSND))
1384 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1386 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1387 subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1388 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1390 if (subhdr->chan & 0x80) { // ?
1391 if (subhdr->chan != 0xff)
1392 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1395 if (!cdr.FileChannelSelected) {
1396 cdr.CurFile = subhdr->file;
1397 cdr.CurChannel = subhdr->chan;
1398 cdr.FileChannelSelected = 1;
1400 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1403 // accepted as adpcm
1408 is_start = !cdr.AdpcmActive;
1409 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, is_start);
1410 if (cdr.AdpcmActive)
1411 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, is_start);
1414 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1418 Croc 2: $40 - only FORM1 (*)
1419 Judge Dredd: $C8 - only FORM1 (*)
1420 Sim Theme Park - no adpcm at all (zero)
1424 cdrReadInterruptSetResult(cdr.StatP);
1426 msfiAdd(cdr.SetSectorPlay, 1);
1427 CDR_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
1429 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1435 bit 2 - adpcm active
1436 bit 5 - 1 result ready
1438 bit 7 - 1 command being processed
1441 unsigned char cdrRead0(void) {
1443 cdr.Ctrl |= cdr.AdpcmActive << 2;
1444 cdr.Ctrl |= cdr.ResultReady << 5;
1446 cdr.Ctrl |= 0x40; // data fifo not empty
1448 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1451 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1453 return psxHu8(0x1800) = cdr.Ctrl;
1456 void cdrWrite0(unsigned char rt) {
1457 CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
1459 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1462 unsigned char cdrRead1(void) {
1463 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1464 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1468 if (cdr.ResultP == cdr.ResultC)
1469 cdr.ResultReady = 0;
1471 CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1473 return psxHu8(0x1801);
1476 void cdrWrite1(unsigned char rt) {
1477 const char *rnames[] = { "0.cmd", "1.smd", "2.smc", "3.arr" }; (void)rnames;
1478 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1480 switch (cdr.Ctrl & 3) {
1484 cdr.AttenuatorRightToRightT = rt;
1490 #ifdef CDR_LOG_CMD_IRQ
1491 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1494 SysPrintf(" Param[%d] = {", cdr.ParamC);
1495 for (i = 0; i < cdr.ParamC; i++)
1496 SysPrintf(" %x,", cdr.Param[i]);
1499 SysPrintf(" @%08x\n", psxRegs.pc);
1502 cdr.ResultReady = 0;
1505 if (!cdr.CmdInProgress) {
1506 cdr.CmdInProgress = rt;
1507 // should be something like 12k + controller delays
1508 set_event(PSXINT_CDR, 5000);
1511 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1512 rt, cdr.Cmd, cdr.CmdInProgress);
1513 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1514 cdr.CmdInProgress = rt;
1520 unsigned char cdrRead2(void) {
1521 unsigned char ret = cdr.Transfer[0x920];
1523 if (cdr.FifoOffset < cdr.FifoSize)
1524 ret = cdr.Transfer[cdr.FifoOffset++];
1526 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1528 CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
1532 void cdrWrite2(unsigned char rt) {
1533 const char *rnames[] = { "0.prm", "1.ien", "2.all", "3.arl" }; (void)rnames;
1534 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1536 switch (cdr.Ctrl & 3) {
1538 if (cdr.ParamC < 8) // FIXME: size and wrapping
1539 cdr.Param[cdr.ParamC++] = rt;
1543 setIrq(cdr.IrqStat, 0x1005);
1546 cdr.AttenuatorLeftToLeftT = rt;
1549 cdr.AttenuatorRightToLeftT = rt;
1554 unsigned char cdrRead3(void) {
1556 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1558 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1560 CDR_LOG_IO("cdr r3.%d.%s: %02x\n", cdr.Ctrl & 3,
1561 (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1562 return psxHu8(0x1803);
1565 void cdrWrite3(unsigned char rt) {
1566 const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
1568 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1570 switch (cdr.Ctrl & 3) {
1574 if (cdr.IrqStat & rt) {
1575 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1576 + psxRegs.intCycle[PSXINT_CDR].cycle;
1577 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1578 #ifdef CDR_LOG_CMD_IRQ
1579 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1580 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1581 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1583 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1584 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1587 if (cdr.CmdInProgress) {
1588 c = 2048 - (psxRegs.cycle - nextCycle);
1589 c = MAX_VALUE(c, 512);
1591 set_event(PSXINT_CDR, c);
1600 cdr.AttenuatorLeftToRightT = rt;
1604 log_unhandled("Mute ADPCM?\n");
1606 ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1607 rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1608 if (ll == cdr.AttenuatorLeftToLeft &&
1609 lr == cdr.AttenuatorLeftToRight &&
1610 rl == cdr.AttenuatorRightToLeft &&
1611 rr == cdr.AttenuatorRightToRight)
1613 cdr.AttenuatorLeftToLeft = ll; cdr.AttenuatorLeftToRight = lr;
1614 cdr.AttenuatorRightToLeft = rl; cdr.AttenuatorRightToRight = rr;
1615 CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1616 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1622 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1623 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1625 else if (rt & 0x80) {
1626 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1627 case MODE_SIZE_2328:
1629 cdr.FifoOffset = 12;
1630 cdr.FifoSize = 2048 + 12;
1633 case MODE_SIZE_2340:
1636 cdr.FifoSize = 2340;
1640 else if (!(rt & 0xc0))
1641 cdr.FifoOffset = DATA_SIZE; // fifo empty
1644 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1645 u32 cdsize, max_words;
1650 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1651 if (cdr.FifoOffset == 0) {
1653 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1658 switch (chcr & 0x71000000) {
1661 ptr = getDmaRam(madr, &max_words);
1662 if (ptr == INVALID_PTR) {
1663 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1667 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1670 GS CDX: Enhancement CD crash
1673 - Spams DMA3 and gets buffer overrun
1675 size = DATA_SIZE - cdr.FifoOffset;
1678 if (size > max_words * 4)
1679 size = max_words * 4;
1682 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1683 cdr.FifoOffset += size;
1685 if (size < cdsize) {
1686 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1687 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1689 psxCpu->Clear(madr, cdsize / 4);
1691 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1693 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1695 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1696 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1700 psxRegs.cycle += (cdsize/4) * 24 - 20;
1705 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1709 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1713 void cdrDmaInterrupt(void)
1715 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1717 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1722 static void getCdInfo(void)
1726 CDR_getTN(cdr.ResultTN);
1727 CDR_getTD(0, cdr.SetSectorEnd);
1728 tmp = cdr.SetSectorEnd[0];
1729 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1730 cdr.SetSectorEnd[2] = tmp;
1734 memset(&cdr, 0, sizeof(cdr));
1737 cdr.FilterChannel = 0;
1739 cdr.IrqStat = NoIntr;
1741 // BIOS player - default values
1742 cdr.AttenuatorLeftToLeft = 0x80;
1743 cdr.AttenuatorLeftToRight = 0x00;
1744 cdr.AttenuatorRightToLeft = 0x00;
1745 cdr.AttenuatorRightToRight = 0x80;
1751 int cdrFreeze(void *f, int Mode) {
1755 if (Mode == 0 && !Config.Cdda)
1758 cdr.freeze_ver = 0x63647202;
1759 gzfreeze(&cdr, sizeof(cdr));
1762 cdr.ParamP = cdr.ParamC;
1763 tmp = cdr.FifoOffset;
1766 gzfreeze(&tmp, sizeof(tmp));
1769 u8 ll = 0, lr = 0, rl = 0, rr = 0;
1772 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1773 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1774 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1775 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1777 // read right sub data
1778 tmpp[0] = btoi(cdr.Prev[0]);
1779 tmpp[1] = btoi(cdr.Prev[1]);
1780 tmpp[2] = btoi(cdr.Prev[2]);
1785 if (cdr.freeze_ver < 0x63647202)
1786 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1788 Find_CurTrack(cdr.SetSectorPlay);
1790 CDR_play(cdr.SetSectorPlay);
1793 ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToLeft,
1794 rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1795 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1801 void LidInterrupt(void) {
1803 cdrLidSeekInterrupt();