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 unused3;
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;
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, // pause, play, read
227 DRIVESTATE_RESCAN_CD,
228 DRIVESTATE_PREPARE_CD,
232 static struct CdrStat stat;
234 static unsigned int msf2sec(const u8 *msf) {
235 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
238 // for that weird psemu API..
239 static unsigned int fsm2sec(const u8 *msf) {
240 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
243 static void sec2msf(unsigned int s, u8 *msf) {
244 msf[0] = s / 75 / 60;
245 s = s - msf[0] * 75 * 60;
251 // cdrPlayReadInterrupt
252 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
254 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
256 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
258 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
259 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
260 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
263 #define StopReading() { \
265 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
268 #define StopCdda() { \
269 if (cdr.Play && !Config.Cdda) CDR_stop(); \
271 cdr.FastForward = 0; \
272 cdr.FastBackward = 0; \
275 #define SetPlaySeekRead(x, f) { \
276 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
280 #define SetResultSize_(size) { \
282 cdr.ResultC = size; \
283 cdr.ResultReady = 1; \
286 #define SetResultSize(size) { \
287 if (cdr.ResultP < cdr.ResultC) \
288 CDR_LOG_I("overwriting result, len=%u\n", cdr.ResultC); \
289 SetResultSize_(size); \
292 static void setIrq(u8 irq, int log_cmd)
294 u8 old = cdr.IrqStat & cdr.IrqMask ? 1 : 0;
295 u8 new_ = irq & cdr.IrqMask ? 1 : 0;
298 if ((old ^ new_) & new_)
299 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
301 #ifdef CDR_LOG_CMD_IRQ
305 CDR_LOG_I("CDR IRQ=%d cmd %02x irqstat %02x: ",
306 !!(cdr.IrqStat & cdr.IrqMask), log_cmd, cdr.IrqStat);
307 for (i = 0; i < cdr.ResultC; i++)
308 SysPrintf("%02x ", cdr.Result[i]);
314 // timing used in this function was taken from tests on real hardware
315 // (yes it's slow, but you probably don't want to modify it)
316 void cdrLidSeekInterrupt(void)
318 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
320 switch (cdr.DriveState) {
322 case DRIVESTATE_STANDBY:
325 SetPlaySeekRead(cdr.StatP, 0);
327 if (CDR_getStatus(&stat) == -1)
330 if (stat.Status & STATUS_SHELLOPEN)
332 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
333 cdr.DriveState = DRIVESTATE_LID_OPEN;
334 set_event(PSXINT_CDRLID, 0x800);
338 case DRIVESTATE_LID_OPEN:
339 if (CDR_getStatus(&stat) == -1)
340 stat.Status &= ~STATUS_SHELLOPEN;
343 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
344 SetPlaySeekRead(cdr.StatP, 0);
345 cdr.StatP |= STATUS_SHELLOPEN;
347 // IIRC this sometimes doesn't happen on real hw
348 // (when lots of commands are sent?)
352 cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
353 cdr.Result[1] = ERROR_SHELLOPEN;
354 setIrq(DiskError, 0x1006);
356 if (cdr.CmdInProgress) {
357 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
358 cdr.CmdInProgress = 0;
360 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
361 cdr.Result[1] = ERROR_NOTREADY;
362 setIrq(DiskError, 0x1007);
365 set_event(PSXINT_CDRLID, cdReadTime * 30);
368 else if (cdr.StatP & STATUS_ROTATING) {
369 cdr.StatP &= ~STATUS_ROTATING;
371 else if (!(stat.Status & STATUS_SHELLOPEN)) {
375 // cdr.StatP STATUS_SHELLOPEN is "sticky"
376 // and is only cleared by CdlNop
378 cdr.DriveState = DRIVESTATE_RESCAN_CD;
379 set_event(PSXINT_CDRLID, cdReadTime * 105);
384 set_event(PSXINT_CDRLID, cdReadTime * 3);
387 case DRIVESTATE_RESCAN_CD:
388 cdr.StatP |= STATUS_ROTATING;
389 cdr.DriveState = DRIVESTATE_PREPARE_CD;
391 // this is very long on real hardware, over 6 seconds
392 // make it a bit faster here...
393 set_event(PSXINT_CDRLID, cdReadTime * 150);
396 case DRIVESTATE_PREPARE_CD:
397 if (cdr.StatP & STATUS_SEEK) {
398 SetPlaySeekRead(cdr.StatP, 0);
399 cdr.DriveState = DRIVESTATE_STANDBY;
402 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
403 set_event(PSXINT_CDRLID, cdReadTime * 26);
409 static void Find_CurTrack(const u8 *time)
413 current = msf2sec(time);
415 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
416 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
417 sect = fsm2sec(cdr.ResultTD);
418 if (sect - current >= 150)
423 static void generate_subq(const u8 *time)
425 unsigned char start[3], next[3];
426 unsigned int this_s, start_s, next_s, pregap;
429 CDR_getTD(cdr.CurTrack, start);
430 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
432 CDR_getTD(cdr.CurTrack + 1, next);
435 // last track - cd size
437 next[0] = cdr.SetSectorEnd[2];
438 next[1] = cdr.SetSectorEnd[1];
439 next[2] = cdr.SetSectorEnd[0];
442 this_s = msf2sec(time);
443 start_s = fsm2sec(start);
444 next_s = fsm2sec(next);
446 cdr.TrackChanged = FALSE;
448 if (next_s - this_s < pregap) {
449 cdr.TrackChanged = TRUE;
456 relative_s = this_s - start_s;
457 if (relative_s < 0) {
459 relative_s = -relative_s;
461 sec2msf(relative_s, cdr.subq.Relative);
463 cdr.subq.Track = itob(cdr.CurTrack);
464 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
465 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
466 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
467 cdr.subq.Absolute[0] = itob(time[0]);
468 cdr.subq.Absolute[1] = itob(time[1]);
469 cdr.subq.Absolute[2] = itob(time[2]);
472 static int ReadTrack(const u8 *time)
474 unsigned char tmp[3];
477 tmp[0] = itob(time[0]);
478 tmp[1] = itob(time[1]);
479 tmp[2] = itob(time[2]);
481 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
483 if (memcmp(cdr.Prev, tmp, 3) == 0)
486 read_ok = CDR_readTrack(tmp);
488 memcpy(cdr.Prev, tmp, 3);
492 static void UpdateSubq(const u8 *time)
494 const struct SubQ *subq;
495 int s = MSF2SECT(time[0], time[1], time[2]);
501 subq = (struct SubQ *)CDR_getBufferSub(s);
502 if (subq != NULL && cdr.CurTrack == 1) {
503 crc = calcCrc((u8 *)subq + 12, 10);
504 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
505 cdr.subq.Track = subq->TrackNumber;
506 cdr.subq.Index = subq->IndexNumber;
507 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
508 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
511 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
512 time[0], time[1], time[2]);
519 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
520 cdr.subq.Track, cdr.subq.Index,
521 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
522 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
525 static void cdrPlayInterrupt_Autopause()
528 boolean abs_lev_chselect;
531 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
532 CDR_LOG_I("autopause\n");
535 cdr.Result[0] = cdr.StatP;
536 setIrq(DataEnd, 0x1000); // 0x1000 just for logging purposes
539 SetPlaySeekRead(cdr.StatP, 0);
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 // need this stupidly long penalty or else Spyro2 intro desyncs
587 // note: if misapplied this breaks MGS cutscenes among other things
588 if (cyclesSinceRS > cdReadTime * 50)
589 seekTime += cdReadTime * 25;
590 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
591 // and then wants some slack time
592 else if (cyclesSinceRS < cdReadTime *3/2)
593 seekTime += cdReadTime;
595 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
596 CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
597 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime);
601 static u32 cdrAlignTimingHack(u32 cycles)
604 * timing hack for T'ai Fu - Wrath of the Tiger:
605 * The game has a bug where it issues some cdc commands from a low priority
606 * vint handler, however there is a higher priority default bios handler
607 * that acks the vint irq and returns, so game's handler is not reached
608 * (see bios irq handler chains at e004 and the game's irq handling func
609 * at 80036810). For the game to work, vint has to arrive after the bios
610 * vint handler rejects some other irq (of which only cd and rcnt2 are
611 * active), but before the game's handler loop reads I_STAT. The time
612 * window for this is quite small (~1k cycles of so). Apparently this
613 * somehow happens naturally on the real hardware.
615 * Note: always enforcing this breaks other games like Crash PAL version
616 * (inputs get dropped because bios handler doesn't see interrupts).
619 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
621 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
622 vint_rel += PSXCLK / 60;
623 while ((s32)(vint_rel - cycles) < 0)
624 vint_rel += PSXCLK / 60;
628 static void cdrUpdateTransferBuf(const u8 *buf);
629 static void cdrReadInterrupt(void);
630 static void cdrPrepCdda(s16 *buf, int samples);
631 static void cdrAttenuate(s16 *buf, int samples, int stereo);
633 static void msfiAdd(u8 *msfi, u32 count)
647 static void msfiSub(u8 *msfi, u32 count)
651 if ((s8)msfi[2] < 0) {
654 if ((s8)msfi[1] < 0) {
661 void cdrPlayReadInterrupt(void)
663 cdr.LastReadSeekCycles = psxRegs.cycle;
670 if (!cdr.Play) return;
672 CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
673 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
675 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
676 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
677 CDR_LOG_I("end stop\n");
679 SetPlaySeekRead(cdr.StatP, 0);
680 cdr.TrackChanged = TRUE;
683 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
686 if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
687 cdrPlayInterrupt_Autopause();
689 if (!cdr.Muted && cdr.Play && !Config.Cdda) {
690 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
691 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
692 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
695 msfiAdd(cdr.SetSectorPlay, 1);
697 // update for CdlGetlocP/autopause
698 generate_subq(cdr.SetSectorPlay);
700 CDRPLAYREAD_INT(cdReadTime, 0);
703 #define CMD_PART2 0x100
704 #define CMD_WHILE_NOT_READY 0x200
706 void cdrInterrupt(void) {
707 int start_rotating = 0;
709 u32 cycles, seekTime = 0;
710 u32 second_resp_time = 0;
716 u8 IrqStat = Acknowledge;
721 CDR_LOG_I("cmd %02x with irqstat %x\n",
722 cdr.CmdInProgress, cdr.IrqStat);
725 if (cdr.Irq1Pending) {
726 // hand out the "newest" sector, according to nocash
727 cdrUpdateTransferBuf(CDR_getBuffer());
728 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
729 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
730 cdr.CmdInProgress, cdr.Irq1Pending);
732 cdr.Result[0] = cdr.Irq1Pending;
734 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
740 cdr.Result[0] = cdr.StatP;
742 Cmd = cdr.CmdInProgress;
743 cdr.CmdInProgress = 0;
752 switch (cdr.DriveState) {
753 case DRIVESTATE_PREPARE_CD:
755 // Syphon filter 2 expects commands to work shortly after it sees
756 // STATUS_ROTATING, so give up trying to emulate the startup seq
757 cdr.DriveState = DRIVESTATE_STANDBY;
758 cdr.StatP &= ~STATUS_SEEK;
759 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
763 case DRIVESTATE_LID_OPEN:
764 case DRIVESTATE_RESCAN_CD:
765 // no disk or busy with the initial scan, allowed cmds are limited
766 not_ready = CMD_WHILE_NOT_READY;
770 switch (Cmd | not_ready) {
772 case CdlNop + CMD_WHILE_NOT_READY:
773 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
774 cdr.StatP &= ~STATUS_SHELLOPEN;
778 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
779 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
781 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
782 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))
784 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
785 if (++cdr.errorRetryhack > 100)
787 error = ERROR_BAD_ARGNUM;
792 for (i = 0; i < 3; i++)
793 set_loc[i] = btoi(cdr.Param[i]);
794 memcpy(cdr.SetSector, set_loc, 3);
795 cdr.SetSector[3] = 0;
796 cdr.SetlocPending = 1;
797 cdr.errorRetryhack = 0;
806 cdr.FastBackward = 0;
810 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
812 if (ParamC != 0 && cdr.Param[0] != 0) {
813 int track = btoi( cdr.Param[0] );
815 if (track <= cdr.ResultTN[1])
816 cdr.CurTrack = track;
818 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
820 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
821 for (i = 0; i < 3; i++)
822 set_loc[i] = cdr.ResultTD[2 - i];
823 seekTime = cdrSeekTime(set_loc);
824 memcpy(cdr.SetSectorPlay, set_loc, 3);
827 else if (cdr.SetlocPending) {
828 seekTime = cdrSeekTime(cdr.SetSector);
829 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
832 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
833 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
835 cdr.SetlocPending = 0;
838 Rayman: detect track changes
841 Twisted Metal 2: skip PREGAP + starting accurate SubQ
842 - plays tracks without retry play
844 Wild 9: skip PREGAP + starting accurate SubQ
845 - plays tracks without retry play
847 Find_CurTrack(cdr.SetSectorPlay);
848 generate_subq(cdr.SetSectorPlay);
849 cdr.LocL[0] = LOCL_INVALID;
850 cdr.SubqForwardSectors = 1;
851 cdr.TrackChanged = FALSE;
852 cdr.FileChannelSelected = 0;
854 cdr.ReportDelay = 60;
858 CDR_play(cdr.SetSectorPlay);
860 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
862 // BIOS player - set flag again
865 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
870 // TODO: error 80 if stopped
873 // GameShark CD Player: Calls 2x + Play 2x
875 cdr.FastBackward = 0;
881 // GameShark CD Player: Calls 2x + Play 2x
882 cdr.FastBackward = 1;
887 if (cdr.DriveState != DRIVESTATE_STOPPED) {
888 error = ERROR_BAD_ARGNUM;
891 second_resp_time = cdReadTime * 125 / 2;
895 case CdlStandby + CMD_PART2:
901 // grab time for current track
902 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
904 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
905 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
906 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
911 SetPlaySeekRead(cdr.StatP, 0);
912 cdr.StatP &= ~STATUS_ROTATING;
913 cdr.LocL[0] = LOCL_INVALID;
915 second_resp_time = 0x800;
916 if (cdr.DriveState == DRIVESTATE_STANDBY)
917 second_resp_time = cdReadTime * 30 / 2;
919 cdr.DriveState = DRIVESTATE_STOPPED;
922 case CdlStop + CMD_PART2:
930 // how the drive maintains the position while paused is quite
931 // complicated, this is the minimum to make "Bedlam" happy
932 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
936 Gundam Battle Assault 2: much slower (*)
937 - Fixes boot, gameplay
939 Hokuto no Ken 2: slower
940 - Fixes intro + subtitles
942 InuYasha - Feudal Fairy Tale: slower
945 /* Gameblabla - Tightening the timings (as taken from Duckstation).
946 * The timings from Duckstation are based upon hardware tests.
947 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
948 * seems to be timing sensitive as it can depend on the CPU's clock speed.
950 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
952 second_resp_time = 7000;
956 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
958 SetPlaySeekRead(cdr.StatP, 0);
961 case CdlPause + CMD_PART2:
966 case CdlReset + CMD_WHILE_NOT_READY:
969 SetPlaySeekRead(cdr.StatP, 0);
970 cdr.LocL[0] = LOCL_INVALID;
972 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
973 second_resp_time = not_ready ? 70000 : 4100000;
977 case CdlReset + CMD_PART2:
978 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
991 cdr.FilterFile = cdr.Param[0];
992 cdr.FilterChannel = cdr.Param[1];
993 cdr.FileChannelSelected = 0;
997 case CdlSetmode + CMD_WHILE_NOT_READY:
998 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
999 cdr.Mode = cdr.Param[0];
1003 case CdlGetparam + CMD_WHILE_NOT_READY:
1004 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1006 cdr.Result[1] = cdr.Mode;
1008 cdr.Result[3] = cdr.FilterFile;
1009 cdr.Result[4] = cdr.FilterChannel;
1013 if (cdr.LocL[0] == LOCL_INVALID) {
1018 memcpy(cdr.Result, cdr.LocL, 8);
1023 memcpy(&cdr.Result, &cdr.subq, 8);
1026 case CdlReadT: // SetSession?
1028 second_resp_time = cdReadTime * 290 / 4;
1032 case CdlReadT + CMD_PART2:
1037 if (CDR_getTN(cdr.ResultTN) == -1) {
1041 cdr.Result[1] = itob(cdr.ResultTN[0]);
1042 cdr.Result[2] = itob(cdr.ResultTN[1]);
1046 cdr.Track = btoi(cdr.Param[0]);
1047 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1048 error = ERROR_BAD_ARGVAL;
1052 cdr.Result[1] = itob(cdr.ResultTD[2]);
1053 cdr.Result[2] = itob(cdr.ResultTD[1]);
1055 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1062 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1064 seekTime = cdrSeekTime(cdr.SetSector);
1065 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1067 Crusaders of Might and Magic = 0.5x-4x
1068 - fix cutscene speech start
1070 Eggs of Steel = 2x-?
1074 - fix cutscene speech
1079 second_resp_time = cdReadTime + seekTime;
1083 case CdlSeekL + CMD_PART2:
1084 case CdlSeekP + CMD_PART2:
1085 SetPlaySeekRead(cdr.StatP, 0);
1086 cdr.Result[0] = cdr.StatP;
1089 Find_CurTrack(cdr.SetSectorPlay);
1090 read_ok = ReadTrack(cdr.SetSectorPlay);
1091 if (read_ok && (buf = CDR_getBuffer()))
1092 memcpy(cdr.LocL, buf, 8);
1093 UpdateSubq(cdr.SetSectorPlay);
1094 cdr.TrackChanged = FALSE;
1095 cdr.LastReadSeekCycles = psxRegs.cycle;
1099 case CdlTest + CMD_WHILE_NOT_READY:
1100 switch (cdr.Param[0]) {
1101 case 0x20: // System Controller ROM Version
1103 memcpy(cdr.Result, Test20, 4);
1107 memcpy(cdr.Result, Test22, 4);
1109 case 0x23: case 0x24:
1111 memcpy(cdr.Result, Test23, 4);
1117 second_resp_time = 20480;
1120 case CdlID + CMD_PART2:
1122 cdr.Result[0] = cdr.StatP;
1127 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1128 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1129 cdr.Result[1] = 0xc0;
1133 cdr.Result[1] |= 0x10;
1134 if (CdromId[0] == '\0')
1135 cdr.Result[1] |= 0x80;
1137 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1138 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1139 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1141 /* This adds the string "PCSX" in Playstation bios boot screen */
1142 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1147 case CdlInit + CMD_WHILE_NOT_READY:
1150 SetPlaySeekRead(cdr.StatP, 0);
1151 // yes, it really sets STATUS_SHELLOPEN
1152 cdr.StatP |= STATUS_SHELLOPEN;
1153 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1154 set_event(PSXINT_CDRLID, 20480);
1159 case CdlGetQ + CMD_WHILE_NOT_READY:
1163 case CdlReadToc + CMD_WHILE_NOT_READY:
1164 cdr.LocL[0] = LOCL_INVALID;
1165 second_resp_time = cdReadTime * 180 / 4;
1169 case CdlReadToc + CMD_PART2:
1170 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1176 if (cdr.Reading && !cdr.SetlocPending)
1179 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1181 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1182 // Read* acts as play for cdda tracks in cdda mode
1186 if (cdr.SetlocPending) {
1187 seekTime = cdrSeekTime(cdr.SetSector);
1188 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1189 cdr.SetlocPending = 0;
1192 cdr.FileChannelSelected = 0;
1193 cdr.AdpcmActive = 0;
1195 // Fighting Force 2 - update subq time immediately
1197 UpdateSubq(cdr.SetSectorPlay);
1198 cdr.LocL[0] = LOCL_INVALID;
1199 cdr.SubqForwardSectors = 1;
1200 cdr.sectorsRead = 0;
1202 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1204 if (Config.hacks.cdr_read_timing)
1205 cycles = cdrAlignTimingHack(cycles);
1206 CDRPLAYREAD_INT(cycles, 1);
1208 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1214 error = ERROR_INVALIDCMD;
1219 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1220 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1221 IrqStat = DiskError;
1222 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1226 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1227 cdr.DriveState = DRIVESTATE_STANDBY;
1228 cdr.StatP |= STATUS_ROTATING;
1231 if (second_resp_time) {
1232 cdr.CmdInProgress = Cmd | 0x100;
1233 set_event(PSXINT_CDR, second_resp_time);
1235 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1236 cdr.CmdInProgress = cdr.Cmd;
1237 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1240 setIrq(IrqStat, Cmd);
1244 #define ssat32_to_16(v) \
1245 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1247 #define ssat32_to_16(v) do { \
1248 if (v < -32768) v = -32768; \
1249 else if (v > 32767) v = 32767; \
1253 static void cdrPrepCdda(s16 *buf, int samples)
1255 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1257 for (i = 0; i < samples; i++) {
1258 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1259 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1264 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1267 int ll = cdr.AttenuatorLeftToLeft;
1268 int lr = cdr.AttenuatorLeftToRight;
1269 int rl = cdr.AttenuatorRightToLeft;
1270 int rr = cdr.AttenuatorRightToRight;
1272 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1275 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1279 for (i = 0; i < samples; i++) {
1282 l = (l * ll + r * rl) >> 7;
1283 r = (r * rr + l * lr) >> 7;
1291 for (i = 0; i < samples; i++) {
1293 l = l * (ll + rl) >> 7;
1294 //r = r * (rr + lr) >> 7;
1302 static void cdrReadInterruptSetResult(unsigned char result)
1305 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1306 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1307 cdr.CmdInProgress, cdr.IrqStat);
1308 cdr.Irq1Pending = result;
1312 cdr.Result[0] = result;
1313 setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1316 static void cdrUpdateTransferBuf(const u8 *buf)
1320 memcpy(cdr.Transfer, buf, DATA_SIZE);
1321 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1322 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1323 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1324 if (cdr.FifoOffset < 2048 + 12)
1325 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1328 static void cdrReadInterrupt(void)
1330 const struct { u8 file, chan, mode, coding; } *subhdr;
1331 const u8 *buf = NULL;
1332 int deliver_data = 1;
1336 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1337 msfiAdd(subqPos, cdr.SubqForwardSectors);
1338 UpdateSubq(subqPos);
1339 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1340 cdr.SubqForwardSectors++;
1341 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1345 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1346 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1349 read_ok = ReadTrack(cdr.SetSectorPlay);
1351 buf = CDR_getBuffer();
1356 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1357 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1360 memcpy(cdr.LocL, buf, 8);
1362 if (!cdr.IrqStat && !cdr.Irq1Pending)
1363 cdrUpdateTransferBuf(buf);
1365 subhdr = (void *)(buf + 4);
1367 // try to process as adpcm
1368 if (!(cdr.Mode & MODE_STRSND))
1370 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1372 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1373 subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1374 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1376 if (subhdr->chan & 0xe0) { // ?
1377 if (subhdr->chan != 0xff)
1378 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1381 if (!cdr.FileChannelSelected) {
1382 cdr.CurFile = subhdr->file;
1383 cdr.CurChannel = subhdr->chan;
1384 cdr.FileChannelSelected = 1;
1386 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1389 // accepted as adpcm
1394 if (!cdr.Muted && cdr.AdpcmActive) {
1395 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1396 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 0);
1399 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, !cdr.AdpcmActive);
1402 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1406 Croc 2: $40 - only FORM1 (*)
1407 Judge Dredd: $C8 - only FORM1 (*)
1408 Sim Theme Park - no adpcm at all (zero)
1412 cdrReadInterruptSetResult(cdr.StatP);
1414 msfiAdd(cdr.SetSectorPlay, 1);
1416 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1422 bit 2 - adpcm active
1423 bit 5 - 1 result ready
1425 bit 7 - 1 command being processed
1428 unsigned char cdrRead0(void) {
1430 cdr.Ctrl |= cdr.AdpcmActive << 2;
1431 cdr.Ctrl |= cdr.ResultReady << 5;
1433 cdr.Ctrl |= 0x40; // data fifo not empty
1435 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1438 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1440 return psxHu8(0x1800) = cdr.Ctrl;
1443 void cdrWrite0(unsigned char rt) {
1444 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1446 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1449 unsigned char cdrRead1(void) {
1450 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1451 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1455 if (cdr.ResultP == cdr.ResultC)
1456 cdr.ResultReady = 0;
1458 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1460 return psxHu8(0x1801);
1463 void cdrWrite1(unsigned char rt) {
1464 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1465 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1467 switch (cdr.Ctrl & 3) {
1471 cdr.AttenuatorRightToRightT = rt;
1477 #ifdef CDR_LOG_CMD_IRQ
1478 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1481 SysPrintf(" Param[%d] = {", cdr.ParamC);
1482 for (i = 0; i < cdr.ParamC; i++)
1483 SysPrintf(" %x,", cdr.Param[i]);
1490 cdr.ResultReady = 0;
1493 if (!cdr.CmdInProgress) {
1494 cdr.CmdInProgress = rt;
1495 // should be something like 12k + controller delays
1496 set_event(PSXINT_CDR, 5000);
1499 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1500 rt, cdr.Cmd, cdr.CmdInProgress);
1501 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1502 cdr.CmdInProgress = rt;
1508 unsigned char cdrRead2(void) {
1509 unsigned char ret = cdr.Transfer[0x920];
1511 if (cdr.FifoOffset < cdr.FifoSize)
1512 ret = cdr.Transfer[cdr.FifoOffset++];
1514 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1516 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1520 void cdrWrite2(unsigned char rt) {
1521 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1522 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1524 switch (cdr.Ctrl & 3) {
1526 if (cdr.ParamC < 8) // FIXME: size and wrapping
1527 cdr.Param[cdr.ParamC++] = rt;
1531 setIrq(cdr.IrqStat, 0x1005);
1534 cdr.AttenuatorLeftToLeftT = rt;
1537 cdr.AttenuatorRightToLeftT = rt;
1542 unsigned char cdrRead3(void) {
1544 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1546 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1548 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1549 return psxHu8(0x1803);
1552 void cdrWrite3(unsigned char rt) {
1553 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1554 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1556 switch (cdr.Ctrl & 3) {
1560 if (cdr.IrqStat & rt) {
1561 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1562 + psxRegs.intCycle[PSXINT_CDR].cycle;
1563 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1564 #ifdef CDR_LOG_CMD_IRQ
1565 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1566 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1567 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1569 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1570 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1573 if (cdr.CmdInProgress) {
1574 c = 2048 - (psxRegs.cycle - nextCycle);
1575 c = MAX_VALUE(c, 512);
1577 set_event(PSXINT_CDR, c);
1586 cdr.AttenuatorLeftToRightT = rt;
1590 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1591 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1592 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1593 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1599 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1600 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1602 else if (rt & 0x80) {
1603 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1604 case MODE_SIZE_2328:
1606 cdr.FifoOffset = 12;
1607 cdr.FifoSize = 2048 + 12;
1610 case MODE_SIZE_2340:
1613 cdr.FifoSize = 2340;
1617 else if (!(rt & 0xc0))
1618 cdr.FifoOffset = DATA_SIZE; // fifo empty
1621 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1622 u32 cdsize, max_words;
1627 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1628 if (cdr.FifoOffset == 0) {
1630 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1635 switch (chcr & 0x71000000) {
1637 ptr = getDmaRam(madr, &max_words);
1638 if (ptr == INVALID_PTR) {
1639 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1643 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1646 GS CDX: Enhancement CD crash
1649 - Spams DMA3 and gets buffer overrun
1651 size = DATA_SIZE - cdr.FifoOffset;
1654 if (size > max_words * 4)
1655 size = max_words * 4;
1658 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1659 cdr.FifoOffset += size;
1661 if (size < cdsize) {
1662 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1663 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1665 psxCpu->Clear(madr, cdsize / 4);
1667 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1669 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1671 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1672 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1676 psxRegs.cycle += (cdsize/4) * 24 - 20;
1681 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1685 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1689 void cdrDmaInterrupt(void)
1691 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1693 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1698 static void getCdInfo(void)
1702 CDR_getTN(cdr.ResultTN);
1703 CDR_getTD(0, cdr.SetSectorEnd);
1704 tmp = cdr.SetSectorEnd[0];
1705 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1706 cdr.SetSectorEnd[2] = tmp;
1710 memset(&cdr, 0, sizeof(cdr));
1713 cdr.FilterChannel = 0;
1715 cdr.IrqStat = NoIntr;
1716 cdr.FifoOffset = DATA_SIZE; // fifo empty
1718 CDR_getStatus(&stat);
1719 if (stat.Status & STATUS_SHELLOPEN) {
1720 cdr.DriveState = DRIVESTATE_LID_OPEN;
1721 cdr.StatP = STATUS_SHELLOPEN;
1723 else if (CdromId[0] == '\0') {
1724 cdr.DriveState = DRIVESTATE_STOPPED;
1728 cdr.DriveState = DRIVESTATE_STANDBY;
1729 cdr.StatP = STATUS_ROTATING;
1732 // BIOS player - default values
1733 cdr.AttenuatorLeftToLeft = 0x80;
1734 cdr.AttenuatorLeftToRight = 0x00;
1735 cdr.AttenuatorRightToLeft = 0x00;
1736 cdr.AttenuatorRightToRight = 0x80;
1741 int cdrFreeze(void *f, int Mode) {
1745 if (Mode == 0 && !Config.Cdda)
1748 cdr.freeze_ver = 0x63647202;
1749 gzfreeze(&cdr, sizeof(cdr));
1752 cdr.ParamP = cdr.ParamC;
1753 tmp = cdr.FifoOffset;
1756 gzfreeze(&tmp, sizeof(tmp));
1761 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1762 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1763 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1764 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1766 // read right sub data
1767 tmpp[0] = btoi(cdr.Prev[0]);
1768 tmpp[1] = btoi(cdr.Prev[1]);
1769 tmpp[2] = btoi(cdr.Prev[2]);
1774 if (cdr.freeze_ver < 0x63647202)
1775 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1777 Find_CurTrack(cdr.SetSectorPlay);
1779 CDR_play(cdr.SetSectorPlay);
1786 void LidInterrupt(void) {
1788 cdrLidSeekInterrupt();