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];
996 case CdlSetmode + CMD_WHILE_NOT_READY:
997 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
998 cdr.Mode = cdr.Param[0];
1002 case CdlGetparam + CMD_WHILE_NOT_READY:
1003 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1005 cdr.Result[1] = cdr.Mode;
1007 cdr.Result[3] = cdr.FilterFile;
1008 cdr.Result[4] = cdr.FilterChannel;
1012 if (cdr.LocL[0] == LOCL_INVALID) {
1017 memcpy(cdr.Result, cdr.LocL, 8);
1022 memcpy(&cdr.Result, &cdr.subq, 8);
1025 case CdlReadT: // SetSession?
1027 second_resp_time = cdReadTime * 290 / 4;
1031 case CdlReadT + CMD_PART2:
1036 if (CDR_getTN(cdr.ResultTN) == -1) {
1040 cdr.Result[1] = itob(cdr.ResultTN[0]);
1041 cdr.Result[2] = itob(cdr.ResultTN[1]);
1045 cdr.Track = btoi(cdr.Param[0]);
1046 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1047 error = ERROR_BAD_ARGVAL;
1051 cdr.Result[1] = itob(cdr.ResultTD[2]);
1052 cdr.Result[2] = itob(cdr.ResultTD[1]);
1054 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1061 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1063 seekTime = cdrSeekTime(cdr.SetSector);
1064 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1066 Crusaders of Might and Magic = 0.5x-4x
1067 - fix cutscene speech start
1069 Eggs of Steel = 2x-?
1073 - fix cutscene speech
1078 second_resp_time = cdReadTime + seekTime;
1082 case CdlSeekL + CMD_PART2:
1083 case CdlSeekP + CMD_PART2:
1084 SetPlaySeekRead(cdr.StatP, 0);
1085 cdr.Result[0] = cdr.StatP;
1088 Find_CurTrack(cdr.SetSectorPlay);
1089 read_ok = ReadTrack(cdr.SetSectorPlay);
1090 if (read_ok && (buf = CDR_getBuffer()))
1091 memcpy(cdr.LocL, buf, 8);
1092 UpdateSubq(cdr.SetSectorPlay);
1093 cdr.TrackChanged = FALSE;
1094 cdr.LastReadSeekCycles = psxRegs.cycle;
1098 case CdlTest + CMD_WHILE_NOT_READY:
1099 switch (cdr.Param[0]) {
1100 case 0x20: // System Controller ROM Version
1102 memcpy(cdr.Result, Test20, 4);
1106 memcpy(cdr.Result, Test22, 4);
1108 case 0x23: case 0x24:
1110 memcpy(cdr.Result, Test23, 4);
1116 second_resp_time = 20480;
1119 case CdlID + CMD_PART2:
1121 cdr.Result[0] = cdr.StatP;
1126 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1127 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1128 cdr.Result[1] = 0xc0;
1132 cdr.Result[1] |= 0x10;
1133 if (CdromId[0] == '\0')
1134 cdr.Result[1] |= 0x80;
1136 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1137 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1138 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1140 /* This adds the string "PCSX" in Playstation bios boot screen */
1141 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1146 case CdlInit + CMD_WHILE_NOT_READY:
1149 SetPlaySeekRead(cdr.StatP, 0);
1150 // yes, it really sets STATUS_SHELLOPEN
1151 cdr.StatP |= STATUS_SHELLOPEN;
1152 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1153 set_event(PSXINT_CDRLID, 20480);
1158 case CdlGetQ + CMD_WHILE_NOT_READY:
1162 case CdlReadToc + CMD_WHILE_NOT_READY:
1163 cdr.LocL[0] = LOCL_INVALID;
1164 second_resp_time = cdReadTime * 180 / 4;
1168 case CdlReadToc + CMD_PART2:
1169 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1175 if (cdr.Reading && !cdr.SetlocPending)
1178 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1180 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1181 // Read* acts as play for cdda tracks in cdda mode
1185 if (cdr.SetlocPending) {
1186 seekTime = cdrSeekTime(cdr.SetSector);
1187 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1188 cdr.SetlocPending = 0;
1191 cdr.FileChannelSelected = 0;
1192 cdr.AdpcmActive = 0;
1194 // Fighting Force 2 - update subq time immediately
1196 UpdateSubq(cdr.SetSectorPlay);
1197 cdr.LocL[0] = LOCL_INVALID;
1198 cdr.SubqForwardSectors = 1;
1199 cdr.sectorsRead = 0;
1201 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1203 if (Config.hacks.cdr_read_timing)
1204 cycles = cdrAlignTimingHack(cycles);
1205 CDRPLAYREAD_INT(cycles, 1);
1207 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1213 error = ERROR_INVALIDCMD;
1218 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1219 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1220 IrqStat = DiskError;
1221 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1225 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1226 cdr.DriveState = DRIVESTATE_STANDBY;
1227 cdr.StatP |= STATUS_ROTATING;
1230 if (second_resp_time) {
1231 cdr.CmdInProgress = Cmd | 0x100;
1232 set_event(PSXINT_CDR, second_resp_time);
1234 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1235 cdr.CmdInProgress = cdr.Cmd;
1236 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1239 setIrq(IrqStat, Cmd);
1243 #define ssat32_to_16(v) \
1244 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1246 #define ssat32_to_16(v) do { \
1247 if (v < -32768) v = -32768; \
1248 else if (v > 32767) v = 32767; \
1252 static void cdrPrepCdda(s16 *buf, int samples)
1254 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1256 for (i = 0; i < samples; i++) {
1257 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1258 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1263 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1266 int ll = cdr.AttenuatorLeftToLeft;
1267 int lr = cdr.AttenuatorLeftToRight;
1268 int rl = cdr.AttenuatorRightToLeft;
1269 int rr = cdr.AttenuatorRightToRight;
1271 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1274 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1278 for (i = 0; i < samples; i++) {
1281 l = (l * ll + r * rl) >> 7;
1282 r = (r * rr + l * lr) >> 7;
1290 for (i = 0; i < samples; i++) {
1292 l = l * (ll + rl) >> 7;
1293 //r = r * (rr + lr) >> 7;
1301 static void cdrReadInterruptSetResult(unsigned char result)
1304 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1305 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1306 cdr.CmdInProgress, cdr.IrqStat);
1307 cdr.Irq1Pending = result;
1311 cdr.Result[0] = result;
1312 setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1315 static void cdrUpdateTransferBuf(const u8 *buf)
1319 memcpy(cdr.Transfer, buf, DATA_SIZE);
1320 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1321 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1322 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1323 if (cdr.FifoOffset < 2048 + 12)
1324 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1327 static void cdrReadInterrupt(void)
1329 const struct { u8 file, chan, mode, coding; } *subhdr;
1330 const u8 *buf = NULL;
1331 int deliver_data = 1;
1335 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1336 msfiAdd(subqPos, cdr.SubqForwardSectors);
1337 UpdateSubq(subqPos);
1338 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1339 cdr.SubqForwardSectors++;
1340 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1344 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1345 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1348 read_ok = ReadTrack(cdr.SetSectorPlay);
1350 buf = CDR_getBuffer();
1355 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1356 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1359 memcpy(cdr.LocL, buf, 8);
1361 if (!cdr.IrqStat && !cdr.Irq1Pending)
1362 cdrUpdateTransferBuf(buf);
1364 subhdr = (void *)(buf + 4);
1366 // try to process as adpcm
1367 if (!(cdr.Mode & MODE_STRSND))
1369 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1371 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1372 subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1373 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1375 if (subhdr->chan & 0xe0) { // ?
1376 if (subhdr->chan != 0xff)
1377 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1380 if (!cdr.FileChannelSelected) {
1381 cdr.CurFile = subhdr->file;
1382 cdr.CurChannel = subhdr->chan;
1383 cdr.FileChannelSelected = 1;
1385 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1388 // accepted as adpcm
1393 if (!cdr.Muted && cdr.AdpcmActive) {
1394 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1395 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 0);
1398 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, !cdr.AdpcmActive);
1401 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1405 Croc 2: $40 - only FORM1 (*)
1406 Judge Dredd: $C8 - only FORM1 (*)
1407 Sim Theme Park - no adpcm at all (zero)
1411 cdrReadInterruptSetResult(cdr.StatP);
1413 msfiAdd(cdr.SetSectorPlay, 1);
1415 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1421 bit 2 - adpcm active
1422 bit 5 - 1 result ready
1424 bit 7 - 1 command being processed
1427 unsigned char cdrRead0(void) {
1429 cdr.Ctrl |= cdr.AdpcmActive << 2;
1430 cdr.Ctrl |= cdr.ResultReady << 5;
1432 cdr.Ctrl |= 0x40; // data fifo not empty
1434 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1437 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1439 return psxHu8(0x1800) = cdr.Ctrl;
1442 void cdrWrite0(unsigned char rt) {
1443 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1445 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1448 unsigned char cdrRead1(void) {
1449 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1450 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1454 if (cdr.ResultP == cdr.ResultC)
1455 cdr.ResultReady = 0;
1457 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1459 return psxHu8(0x1801);
1462 void cdrWrite1(unsigned char rt) {
1463 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1464 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1466 switch (cdr.Ctrl & 3) {
1470 cdr.AttenuatorRightToRightT = rt;
1476 #ifdef CDR_LOG_CMD_IRQ
1477 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1480 SysPrintf(" Param[%d] = {", cdr.ParamC);
1481 for (i = 0; i < cdr.ParamC; i++)
1482 SysPrintf(" %x,", cdr.Param[i]);
1489 cdr.ResultReady = 0;
1492 if (!cdr.CmdInProgress) {
1493 cdr.CmdInProgress = rt;
1494 // should be something like 12k + controller delays
1495 set_event(PSXINT_CDR, 5000);
1498 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1499 rt, cdr.Cmd, cdr.CmdInProgress);
1500 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1501 cdr.CmdInProgress = rt;
1507 unsigned char cdrRead2(void) {
1508 unsigned char ret = cdr.Transfer[0x920];
1510 if (cdr.FifoOffset < cdr.FifoSize)
1511 ret = cdr.Transfer[cdr.FifoOffset++];
1513 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1515 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1519 void cdrWrite2(unsigned char rt) {
1520 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1521 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1523 switch (cdr.Ctrl & 3) {
1525 if (cdr.ParamC < 8) // FIXME: size and wrapping
1526 cdr.Param[cdr.ParamC++] = rt;
1530 setIrq(cdr.IrqStat, 0x1005);
1533 cdr.AttenuatorLeftToLeftT = rt;
1536 cdr.AttenuatorRightToLeftT = rt;
1541 unsigned char cdrRead3(void) {
1543 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1545 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1547 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1548 return psxHu8(0x1803);
1551 void cdrWrite3(unsigned char rt) {
1552 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1553 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1555 switch (cdr.Ctrl & 3) {
1559 if (cdr.IrqStat & rt) {
1560 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1561 + psxRegs.intCycle[PSXINT_CDR].cycle;
1562 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1563 #ifdef CDR_LOG_CMD_IRQ
1564 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1565 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1566 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1568 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1569 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1572 if (cdr.CmdInProgress) {
1573 c = 2048 - (psxRegs.cycle - nextCycle);
1574 c = MAX_VALUE(c, 512);
1576 set_event(PSXINT_CDR, c);
1585 cdr.AttenuatorLeftToRightT = rt;
1589 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1590 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1591 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1592 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1598 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1599 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1601 else if (rt & 0x80) {
1602 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1603 case MODE_SIZE_2328:
1605 cdr.FifoOffset = 12;
1606 cdr.FifoSize = 2048 + 12;
1609 case MODE_SIZE_2340:
1612 cdr.FifoSize = 2340;
1616 else if (!(rt & 0xc0))
1617 cdr.FifoOffset = DATA_SIZE; // fifo empty
1620 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1621 u32 cdsize, max_words;
1626 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1627 if (cdr.FifoOffset == 0) {
1629 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1634 switch (chcr & 0x71000000) {
1636 ptr = getDmaRam(madr, &max_words);
1637 if (ptr == INVALID_PTR) {
1638 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1642 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1645 GS CDX: Enhancement CD crash
1648 - Spams DMA3 and gets buffer overrun
1650 size = DATA_SIZE - cdr.FifoOffset;
1653 if (size > max_words * 4)
1654 size = max_words * 4;
1657 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1658 cdr.FifoOffset += size;
1660 if (size < cdsize) {
1661 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1662 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1664 psxCpu->Clear(madr, cdsize / 4);
1666 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1668 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1670 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1671 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1675 psxRegs.cycle += (cdsize/4) * 24 - 20;
1680 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1684 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1688 void cdrDmaInterrupt(void)
1690 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1692 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1697 static void getCdInfo(void)
1701 CDR_getTN(cdr.ResultTN);
1702 CDR_getTD(0, cdr.SetSectorEnd);
1703 tmp = cdr.SetSectorEnd[0];
1704 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1705 cdr.SetSectorEnd[2] = tmp;
1709 memset(&cdr, 0, sizeof(cdr));
1712 cdr.FilterChannel = 0;
1714 cdr.IrqStat = NoIntr;
1715 cdr.FifoOffset = DATA_SIZE; // fifo empty
1717 CDR_getStatus(&stat);
1718 if (stat.Status & STATUS_SHELLOPEN) {
1719 cdr.DriveState = DRIVESTATE_LID_OPEN;
1720 cdr.StatP = STATUS_SHELLOPEN;
1722 else if (CdromId[0] == '\0') {
1723 cdr.DriveState = DRIVESTATE_STOPPED;
1727 cdr.DriveState = DRIVESTATE_STANDBY;
1728 cdr.StatP = STATUS_ROTATING;
1731 // BIOS player - default values
1732 cdr.AttenuatorLeftToLeft = 0x80;
1733 cdr.AttenuatorLeftToRight = 0x00;
1734 cdr.AttenuatorRightToLeft = 0x00;
1735 cdr.AttenuatorRightToRight = 0x80;
1740 int cdrFreeze(void *f, int Mode) {
1744 if (Mode == 0 && !Config.Cdda)
1747 cdr.freeze_ver = 0x63647202;
1748 gzfreeze(&cdr, sizeof(cdr));
1751 cdr.ParamP = cdr.ParamC;
1752 tmp = cdr.FifoOffset;
1755 gzfreeze(&tmp, sizeof(tmp));
1760 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1761 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1762 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1763 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1765 // read right sub data
1766 tmpp[0] = btoi(cdr.Prev[0]);
1767 tmpp[1] = btoi(cdr.Prev[1]);
1768 tmpp[2] = btoi(cdr.Prev[2]);
1773 if (cdr.freeze_ver < 0x63647202)
1774 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1776 Find_CurTrack(cdr.SetSectorPlay);
1778 CDR_play(cdr.SetSectorPlay);
1785 void LidInterrupt(void) {
1787 cdrLidSeekInterrupt();