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;
56 unsigned char unused2;
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];
97 int Mode, File, Channel;
98 unsigned char LocL[8];
109 u32 LastReadSeekCycles;
118 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
119 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
120 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
121 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
123 static s16 read_buf[CD_FRAMESIZE_RAW/2];
125 /* CD-ROM magic numbers */
126 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
131 #define CdlBackward 5
139 #define CdlSetfilter 13
140 #define CdlSetmode 14
141 #define CdlGetparam 15
142 #define CdlGetlocL 16
143 #define CdlGetlocP 17
149 #define CdlSetclock 23
150 #define CdlGetclock 24
156 #define CdlReadToc 30
158 #ifdef CDR_LOG_CMD_IRQ
159 static const char * const CmdName[0x100] = {
160 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
161 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
162 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
163 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
164 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
165 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
166 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
167 "CdlInit", NULL, "CDlReadToc", NULL
171 unsigned char Test04[] = { 0 };
172 unsigned char Test05[] = { 0 };
173 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
174 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
175 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
181 #define Acknowledge 3
186 #define MODE_SPEED (1<<7) // 0x80
187 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
188 #define MODE_SIZE_2340 (1<<5) // 0x20
189 #define MODE_SIZE_2328 (1<<4) // 0x10
190 #define MODE_SIZE_2048 (0<<4) // 0x00
191 #define MODE_SF (1<<3) // 0x08 channel on/off
192 #define MODE_REPORT (1<<2) // 0x04
193 #define MODE_AUTOPAUSE (1<<1) // 0x02
194 #define MODE_CDDA (1<<0) // 0x01
197 #define STATUS_PLAY (1<<7) // 0x80
198 #define STATUS_SEEK (1<<6) // 0x40
199 #define STATUS_READ (1<<5) // 0x20
200 #define STATUS_SHELLOPEN (1<<4) // 0x10
201 #define STATUS_UNKNOWN3 (1<<3) // 0x08
202 #define STATUS_SEEKERROR (1<<2) // 0x04
203 #define STATUS_ROTATING (1<<1) // 0x02
204 #define STATUS_ERROR (1<<0) // 0x01
207 #define ERROR_NOTREADY (1<<7) // 0x80
208 #define ERROR_INVALIDCMD (1<<6) // 0x40
209 #define ERROR_INVALIDARG (1<<5) // 0x20
210 #define ERROR_SHELLOPEN (1<<3) // 0x08
212 // 1x = 75 sectors per second
213 // PSXCLK = 1 sec in the ps
214 // so (PSXCLK / 75) = cdr read time (linuzappz)
215 #define cdReadTime (PSXCLK / 75)
217 #define LOCL_INVALID 0xff
218 #define SUBQ_FORWARD_SECTORS 2u
221 DRIVESTATE_STANDBY = 0, // pause, play, read
223 DRIVESTATE_RESCAN_CD,
224 DRIVESTATE_PREPARE_CD,
228 static struct CdrStat stat;
230 static unsigned int msf2sec(const u8 *msf) {
231 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
234 // for that weird psemu API..
235 static unsigned int fsm2sec(const u8 *msf) {
236 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
239 static void sec2msf(unsigned int s, u8 *msf) {
240 msf[0] = s / 75 / 60;
241 s = s - msf[0] * 75 * 60;
247 // cdrPlayReadInterrupt
248 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
250 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
252 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
254 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
255 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
256 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
259 #define StopReading() { \
261 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
264 #define StopCdda() { \
265 if (cdr.Play && !Config.Cdda) CDR_stop(); \
267 cdr.FastForward = 0; \
268 cdr.FastBackward = 0; \
271 #define SetPlaySeekRead(x, f) { \
272 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
276 #define SetResultSize(size) { \
278 cdr.ResultC = size; \
279 cdr.ResultReady = 1; \
282 static void setIrq(int log_cmd)
284 if (cdr.Stat & cdr.Reg2)
285 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
287 #ifdef CDR_LOG_CMD_IRQ
291 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
292 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
293 for (i = 0; i < cdr.ResultC; i++)
294 SysPrintf("%02x ", cdr.Result[i]);
300 // timing used in this function was taken from tests on real hardware
301 // (yes it's slow, but you probably don't want to modify it)
302 void cdrLidSeekInterrupt(void)
304 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
306 switch (cdr.DriveState) {
308 case DRIVESTATE_STANDBY:
311 SetPlaySeekRead(cdr.StatP, 0);
313 if (CDR_getStatus(&stat) == -1)
316 if (stat.Status & STATUS_SHELLOPEN)
318 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
319 cdr.DriveState = DRIVESTATE_LID_OPEN;
320 set_event(PSXINT_CDRLID, 0x800);
324 case DRIVESTATE_LID_OPEN:
325 if (CDR_getStatus(&stat) == -1)
326 stat.Status &= ~STATUS_SHELLOPEN;
329 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
330 SetPlaySeekRead(cdr.StatP, 0);
331 cdr.StatP |= STATUS_SHELLOPEN;
333 // IIRC this sometimes doesn't happen on real hw
334 // (when lots of commands are sent?)
338 cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
339 cdr.Result[1] = ERROR_SHELLOPEN;
340 cdr.Stat = DiskError;
343 if (cdr.CmdInProgress) {
344 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
345 cdr.CmdInProgress = 0;
347 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
348 cdr.Result[1] = ERROR_NOTREADY;
349 cdr.Stat = DiskError;
353 set_event(PSXINT_CDRLID, cdReadTime * 30);
356 else if (cdr.StatP & STATUS_ROTATING) {
357 cdr.StatP &= ~STATUS_ROTATING;
359 else if (!(stat.Status & STATUS_SHELLOPEN)) {
363 // cdr.StatP STATUS_SHELLOPEN is "sticky"
364 // and is only cleared by CdlNop
366 cdr.DriveState = DRIVESTATE_RESCAN_CD;
367 set_event(PSXINT_CDRLID, cdReadTime * 105);
372 set_event(PSXINT_CDRLID, cdReadTime * 3);
375 case DRIVESTATE_RESCAN_CD:
376 cdr.StatP |= STATUS_ROTATING;
377 cdr.DriveState = DRIVESTATE_PREPARE_CD;
379 // this is very long on real hardware, over 6 seconds
380 // make it a bit faster here...
381 set_event(PSXINT_CDRLID, cdReadTime * 150);
384 case DRIVESTATE_PREPARE_CD:
385 if (cdr.StatP & STATUS_SEEK) {
386 SetPlaySeekRead(cdr.StatP, 0);
387 cdr.DriveState = DRIVESTATE_STANDBY;
390 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
391 set_event(PSXINT_CDRLID, cdReadTime * 26);
397 static void Find_CurTrack(const u8 *time)
401 current = msf2sec(time);
403 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
404 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
405 sect = fsm2sec(cdr.ResultTD);
406 if (sect - current >= 150)
411 static void generate_subq(const u8 *time)
413 unsigned char start[3], next[3];
414 unsigned int this_s, start_s, next_s, pregap;
417 CDR_getTD(cdr.CurTrack, start);
418 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
420 CDR_getTD(cdr.CurTrack + 1, next);
423 // last track - cd size
425 next[0] = cdr.SetSectorEnd[2];
426 next[1] = cdr.SetSectorEnd[1];
427 next[2] = cdr.SetSectorEnd[0];
430 this_s = msf2sec(time);
431 start_s = fsm2sec(start);
432 next_s = fsm2sec(next);
434 cdr.TrackChanged = FALSE;
436 if (next_s - this_s < pregap) {
437 cdr.TrackChanged = TRUE;
444 relative_s = this_s - start_s;
445 if (relative_s < 0) {
447 relative_s = -relative_s;
449 sec2msf(relative_s, cdr.subq.Relative);
451 cdr.subq.Track = itob(cdr.CurTrack);
452 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
453 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
454 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
455 cdr.subq.Absolute[0] = itob(time[0]);
456 cdr.subq.Absolute[1] = itob(time[1]);
457 cdr.subq.Absolute[2] = itob(time[2]);
460 static int ReadTrack(const u8 *time)
462 unsigned char tmp[3];
465 tmp[0] = itob(time[0]);
466 tmp[1] = itob(time[1]);
467 tmp[2] = itob(time[2]);
469 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
471 if (memcmp(cdr.Prev, tmp, 3) == 0)
474 read_ok = CDR_readTrack(tmp);
476 memcpy(cdr.Prev, tmp, 3);
480 static void UpdateSubq(const u8 *time)
482 const struct SubQ *subq;
483 int s = MSF2SECT(time[0], time[1], time[2]);
489 subq = (struct SubQ *)CDR_getBufferSub(s);
490 if (subq != NULL && cdr.CurTrack == 1) {
491 crc = calcCrc((u8 *)subq + 12, 10);
492 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
493 cdr.subq.Track = subq->TrackNumber;
494 cdr.subq.Index = subq->IndexNumber;
495 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
496 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
499 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
500 time[0], time[1], time[2]);
507 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
508 cdr.subq.Track, cdr.subq.Index,
509 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
510 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
513 static void cdrPlayInterrupt_Autopause()
516 boolean abs_lev_chselect;
519 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
520 CDR_LOG_I("autopause\n");
523 cdr.Result[0] = cdr.StatP;
525 setIrq(0x1000); // 0x1000 just for logging purposes
528 SetPlaySeekRead(cdr.StatP, 0);
530 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
531 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
534 cdr.Result[0] = cdr.StatP;
535 cdr.Result[1] = cdr.subq.Track;
536 cdr.Result[2] = cdr.subq.Index;
538 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
540 /* 8 is a hack. For accuracy, it should be 588. */
541 for (i = 0; i < 8; i++)
543 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
545 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
546 abs_lev_max |= abs_lev_chselect << 15;
548 if (cdr.subq.Absolute[2] & 0x10) {
549 cdr.Result[3] = cdr.subq.Relative[0];
550 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
551 cdr.Result[5] = cdr.subq.Relative[2];
554 cdr.Result[3] = cdr.subq.Absolute[0];
555 cdr.Result[4] = cdr.subq.Absolute[1];
556 cdr.Result[5] = cdr.subq.Absolute[2];
558 cdr.Result[6] = abs_lev_max >> 0;
559 cdr.Result[7] = abs_lev_max >> 8;
561 cdr.Stat = DataReady;
569 static int cdrSeekTime(unsigned char *target)
571 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
572 int seekTime = abs(diff) * (cdReadTime / 2000);
573 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
574 seekTime = MAX_VALUE(seekTime, 20000);
576 // need this stupidly long penalty or else Spyro2 intro desyncs
577 // note: if misapplied this breaks MGS cutscenes among other things
578 if (cyclesSinceRS > cdReadTime * 50)
579 seekTime += cdReadTime * 25;
580 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
581 // and then wants some slack time
582 else if (cyclesSinceRS < cdReadTime *3/2)
583 seekTime += cdReadTime;
585 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
586 CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
587 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime);
591 static u32 cdrAlignTimingHack(u32 cycles)
594 * timing hack for T'ai Fu - Wrath of the Tiger:
595 * The game has a bug where it issues some cdc commands from a low priority
596 * vint handler, however there is a higher priority default bios handler
597 * that acks the vint irq and returns, so game's handler is not reached
598 * (see bios irq handler chains at e004 and the game's irq handling func
599 * at 80036810). For the game to work, vint has to arrive after the bios
600 * vint handler rejects some other irq (of which only cd and rcnt2 are
601 * active), but before the game's handler loop reads I_STAT. The time
602 * window for this is quite small (~1k cycles of so). Apparently this
603 * somehow happens naturally on the real hardware.
605 * Note: always enforcing this breaks other games like Crash PAL version
606 * (inputs get dropped because bios handler doesn't see interrupts).
609 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
611 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
612 vint_rel += PSXCLK / 60;
613 while ((s32)(vint_rel - cycles) < 0)
614 vint_rel += PSXCLK / 60;
618 static void cdrUpdateTransferBuf(const u8 *buf);
619 static void cdrReadInterrupt(void);
620 static void cdrPrepCdda(s16 *buf, int samples);
621 static void cdrAttenuate(s16 *buf, int samples, int stereo);
623 static void msfiAdd(u8 *msfi, u32 count)
637 static void msfiSub(u8 *msfi, u32 count)
641 if ((s8)msfi[2] < 0) {
644 if ((s8)msfi[1] < 0) {
651 void cdrPlayReadInterrupt(void)
653 cdr.LastReadSeekCycles = psxRegs.cycle;
660 if (!cdr.Play) return;
662 CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
663 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
665 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
666 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
667 CDR_LOG_I("end stop\n");
669 SetPlaySeekRead(cdr.StatP, 0);
670 cdr.TrackChanged = TRUE;
673 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
676 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
677 cdrPlayInterrupt_Autopause();
679 if (!cdr.Muted && cdr.Play && !Config.Cdda) {
680 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
681 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
682 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
686 msfiAdd(cdr.SetSectorPlay, 1);
688 // update for CdlGetlocP/autopause
689 generate_subq(cdr.SetSectorPlay);
691 CDRPLAYREAD_INT(cdReadTime, 0);
694 #define CMD_PART2 0x100
695 #define CMD_WHILE_NOT_READY 0x200
697 void cdrInterrupt(void) {
698 int start_rotating = 0;
700 u32 cycles, seekTime = 0;
701 u32 second_resp_time = 0;
711 CDR_LOG_I("cmd %02x with irqstat %x\n",
712 cdr.CmdInProgress, cdr.Stat);
715 if (cdr.Irq1Pending) {
716 // hand out the "newest" sector, according to nocash
717 cdrUpdateTransferBuf(CDR_getBuffer());
718 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
719 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
720 cdr.CmdInProgress, cdr.Irq1Pending);
722 cdr.Result[0] = cdr.Irq1Pending;
723 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
731 cdr.Result[0] = cdr.StatP;
732 cdr.Stat = Acknowledge;
734 Cmd = cdr.CmdInProgress;
735 cdr.CmdInProgress = 0;
744 switch (cdr.DriveState) {
745 case DRIVESTATE_PREPARE_CD:
747 // Syphon filter 2 expects commands to work shortly after it sees
748 // STATUS_ROTATING, so give up trying to emulate the startup seq
749 cdr.DriveState = DRIVESTATE_STANDBY;
750 cdr.StatP &= ~STATUS_SEEK;
751 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
755 case DRIVESTATE_LID_OPEN:
756 case DRIVESTATE_RESCAN_CD:
757 // no disk or busy with the initial scan, allowed cmds are limited
758 not_ready = CMD_WHILE_NOT_READY;
762 switch (Cmd | not_ready) {
764 case CdlNop + CMD_WHILE_NOT_READY:
765 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
766 cdr.StatP &= ~STATUS_SHELLOPEN;
770 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
771 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
773 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
774 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))
776 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
777 if (++cdr.errorRetryhack > 100)
779 error = ERROR_INVALIDARG;
784 for (i = 0; i < 3; i++)
785 set_loc[i] = btoi(cdr.Param[i]);
786 memcpy(cdr.SetSector, set_loc, 3);
787 cdr.SetSector[3] = 0;
788 cdr.SetlocPending = 1;
789 cdr.errorRetryhack = 0;
798 cdr.FastBackward = 0;
802 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
804 if (ParamC != 0 && cdr.Param[0] != 0) {
805 int track = btoi( cdr.Param[0] );
807 if (track <= cdr.ResultTN[1])
808 cdr.CurTrack = track;
810 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
812 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
813 for (i = 0; i < 3; i++)
814 set_loc[i] = cdr.ResultTD[2 - i];
815 seekTime = cdrSeekTime(set_loc);
816 memcpy(cdr.SetSectorPlay, set_loc, 3);
819 else if (cdr.SetlocPending) {
820 seekTime = cdrSeekTime(cdr.SetSector);
821 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
824 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
825 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
827 cdr.SetlocPending = 0;
830 Rayman: detect track changes
833 Twisted Metal 2: skip PREGAP + starting accurate SubQ
834 - plays tracks without retry play
836 Wild 9: skip PREGAP + starting accurate SubQ
837 - plays tracks without retry play
839 Find_CurTrack(cdr.SetSectorPlay);
840 generate_subq(cdr.SetSectorPlay);
841 cdr.LocL[0] = LOCL_INVALID;
842 cdr.SubqForwardSectors = 1;
843 cdr.TrackChanged = FALSE;
845 cdr.ReportDelay = 60;
849 CDR_play(cdr.SetSectorPlay);
851 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
853 // BIOS player - set flag again
856 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
861 // TODO: error 80 if stopped
864 // GameShark CD Player: Calls 2x + Play 2x
866 cdr.FastBackward = 0;
872 // GameShark CD Player: Calls 2x + Play 2x
873 cdr.FastBackward = 1;
878 if (cdr.DriveState != DRIVESTATE_STOPPED) {
879 error = ERROR_INVALIDARG;
882 second_resp_time = cdReadTime * 125 / 2;
886 case CdlStandby + CMD_PART2:
892 // grab time for current track
893 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
895 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
896 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
897 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
902 SetPlaySeekRead(cdr.StatP, 0);
903 cdr.StatP &= ~STATUS_ROTATING;
904 cdr.LocL[0] = LOCL_INVALID;
906 second_resp_time = 0x800;
907 if (cdr.DriveState == DRIVESTATE_STANDBY)
908 second_resp_time = cdReadTime * 30 / 2;
910 cdr.DriveState = DRIVESTATE_STOPPED;
913 case CdlStop + CMD_PART2:
921 // how the drive maintains the position while paused is quite
922 // complicated, this is the minimum to make "Bedlam" happy
923 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
927 Gundam Battle Assault 2: much slower (*)
928 - Fixes boot, gameplay
930 Hokuto no Ken 2: slower
931 - Fixes intro + subtitles
933 InuYasha - Feudal Fairy Tale: slower
936 /* Gameblabla - Tightening the timings (as taken from Duckstation).
937 * The timings from Duckstation are based upon hardware tests.
938 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
939 * seems to be timing sensitive as it can depend on the CPU's clock speed.
941 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
943 second_resp_time = 7000;
947 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
949 SetPlaySeekRead(cdr.StatP, 0);
952 case CdlPause + CMD_PART2:
957 case CdlReset + CMD_WHILE_NOT_READY:
960 SetPlaySeekRead(cdr.StatP, 0);
961 cdr.LocL[0] = LOCL_INVALID;
963 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
964 second_resp_time = not_ready ? 70000 : 4100000;
968 case CdlReset + CMD_PART2:
969 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
982 cdr.File = cdr.Param[0];
983 cdr.Channel = cdr.Param[1];
987 case CdlSetmode + CMD_WHILE_NOT_READY:
988 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
989 cdr.Mode = cdr.Param[0];
993 case CdlGetparam + CMD_WHILE_NOT_READY:
994 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
996 cdr.Result[1] = cdr.Mode;
998 cdr.Result[3] = cdr.File;
999 cdr.Result[4] = cdr.Channel;
1003 if (cdr.LocL[0] == LOCL_INVALID) {
1008 memcpy(cdr.Result, cdr.LocL, 8);
1013 memcpy(&cdr.Result, &cdr.subq, 8);
1016 case CdlReadT: // SetSession?
1018 second_resp_time = cdReadTime * 290 / 4;
1022 case CdlReadT + CMD_PART2:
1023 cdr.Stat = Complete;
1028 if (CDR_getTN(cdr.ResultTN) == -1) {
1029 cdr.Stat = DiskError;
1030 cdr.Result[0] |= STATUS_ERROR;
1032 cdr.Stat = Acknowledge;
1033 cdr.Result[1] = itob(cdr.ResultTN[0]);
1034 cdr.Result[2] = itob(cdr.ResultTN[1]);
1039 cdr.Track = btoi(cdr.Param[0]);
1041 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1042 cdr.Stat = DiskError;
1043 cdr.Result[0] |= STATUS_ERROR;
1045 cdr.Stat = Acknowledge;
1046 cdr.Result[0] = cdr.StatP;
1047 cdr.Result[1] = itob(cdr.ResultTD[2]);
1048 cdr.Result[2] = itob(cdr.ResultTD[1]);
1049 /* According to Nocash's documentation, the function doesn't care about ff.
1050 * This can be seen also in Mednafen's implementation. */
1051 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1059 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1061 seekTime = cdrSeekTime(cdr.SetSector);
1062 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1064 Crusaders of Might and Magic = 0.5x-4x
1065 - fix cutscene speech start
1067 Eggs of Steel = 2x-?
1071 - fix cutscene speech
1076 second_resp_time = cdReadTime + seekTime;
1080 case CdlSeekL + CMD_PART2:
1081 case CdlSeekP + CMD_PART2:
1082 SetPlaySeekRead(cdr.StatP, 0);
1083 cdr.Result[0] = cdr.StatP;
1084 cdr.Stat = Complete;
1086 Find_CurTrack(cdr.SetSectorPlay);
1087 read_ok = ReadTrack(cdr.SetSectorPlay);
1088 if (read_ok && (buf = CDR_getBuffer()))
1089 memcpy(cdr.LocL, buf, 8);
1090 UpdateSubq(cdr.SetSectorPlay);
1091 cdr.TrackChanged = FALSE;
1092 cdr.LastReadSeekCycles = psxRegs.cycle;
1096 case CdlTest + CMD_WHILE_NOT_READY:
1097 switch (cdr.Param[0]) {
1098 case 0x20: // System Controller ROM Version
1100 memcpy(cdr.Result, Test20, 4);
1104 memcpy(cdr.Result, Test22, 4);
1106 case 0x23: case 0x24:
1108 memcpy(cdr.Result, Test23, 4);
1114 second_resp_time = 20480;
1117 case CdlID + CMD_PART2:
1119 cdr.Result[0] = cdr.StatP;
1124 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1125 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1126 cdr.Result[1] = 0xc0;
1130 cdr.Result[1] |= 0x10;
1131 if (CdromId[0] == '\0')
1132 cdr.Result[1] |= 0x80;
1134 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1135 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1136 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1138 /* This adds the string "PCSX" in Playstation bios boot screen */
1139 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1140 cdr.Stat = Complete;
1144 case CdlInit + CMD_WHILE_NOT_READY:
1147 SetPlaySeekRead(cdr.StatP, 0);
1148 // yes, it really sets STATUS_SHELLOPEN
1149 cdr.StatP |= STATUS_SHELLOPEN;
1150 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1151 set_event(PSXINT_CDRLID, 20480);
1156 case CdlGetQ + CMD_WHILE_NOT_READY:
1160 case CdlReadToc + CMD_WHILE_NOT_READY:
1161 cdr.LocL[0] = LOCL_INVALID;
1162 second_resp_time = cdReadTime * 180 / 4;
1166 case CdlReadToc + CMD_PART2:
1167 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1168 cdr.Stat = Complete;
1173 if (cdr.Reading && !cdr.SetlocPending)
1176 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1178 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1179 // Read* acts as play for cdda tracks in cdda mode
1183 if (cdr.SetlocPending) {
1184 seekTime = cdrSeekTime(cdr.SetSector);
1185 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1186 cdr.SetlocPending = 0;
1189 cdr.FirstSector = 1;
1191 // Fighting Force 2 - update subq time immediately
1193 UpdateSubq(cdr.SetSectorPlay);
1194 cdr.LocL[0] = LOCL_INVALID;
1195 cdr.SubqForwardSectors = 1;
1196 cdr.sectorsRead = 0;
1198 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1200 if (Config.hacks.cdr_read_timing)
1201 cycles = cdrAlignTimingHack(cycles);
1202 CDRPLAYREAD_INT(cycles, 1);
1204 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1210 error = ERROR_INVALIDCMD;
1215 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1216 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1217 cdr.Stat = DiskError;
1218 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1222 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1223 cdr.DriveState = DRIVESTATE_STANDBY;
1224 cdr.StatP |= STATUS_ROTATING;
1227 if (second_resp_time) {
1228 cdr.CmdInProgress = Cmd | 0x100;
1229 set_event(PSXINT_CDR, second_resp_time);
1231 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1232 cdr.CmdInProgress = cdr.Cmd;
1233 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1240 #define ssat32_to_16(v) \
1241 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1243 #define ssat32_to_16(v) do { \
1244 if (v < -32768) v = -32768; \
1245 else if (v > 32767) v = 32767; \
1249 static void cdrPrepCdda(s16 *buf, int samples)
1251 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1253 for (i = 0; i < samples; i++) {
1254 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1255 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1260 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1263 int ll = cdr.AttenuatorLeftToLeft;
1264 int lr = cdr.AttenuatorLeftToRight;
1265 int rl = cdr.AttenuatorRightToLeft;
1266 int rr = cdr.AttenuatorRightToRight;
1268 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1271 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1275 for (i = 0; i < samples; i++) {
1278 l = (l * ll + r * rl) >> 7;
1279 r = (r * rr + l * lr) >> 7;
1287 for (i = 0; i < samples; i++) {
1289 l = l * (ll + rl) >> 7;
1290 //r = r * (rr + lr) >> 7;
1298 static void cdrReadInterruptSetResult(unsigned char result)
1301 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1302 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1303 cdr.CmdInProgress, cdr.Stat);
1304 cdr.Irq1Pending = result;
1308 cdr.Result[0] = result;
1309 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1313 static void cdrUpdateTransferBuf(const u8 *buf)
1317 memcpy(cdr.Transfer, buf, DATA_SIZE);
1318 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1319 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1320 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1321 if (cdr.FifoOffset < 2048 + 12)
1322 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1325 static void cdrReadInterrupt(void)
1327 u8 *buf = NULL, *hdr;
1331 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1332 msfiAdd(subqPos, cdr.SubqForwardSectors);
1333 UpdateSubq(subqPos);
1334 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1335 cdr.SubqForwardSectors++;
1336 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1340 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1341 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1344 read_ok = ReadTrack(cdr.SetSectorPlay);
1346 buf = CDR_getBuffer();
1351 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1352 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1355 memcpy(cdr.LocL, buf, 8);
1357 if (!cdr.Stat && !cdr.Irq1Pending)
1358 cdrUpdateTransferBuf(buf);
1360 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1362 // Firemen 2: Multi-XA files - briefings, cutscenes
1363 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1365 cdr.Channel = hdr[1];
1369 * Skips playing on channel 255.
1370 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1371 * TODO : Check if this is the proper behaviour.
1373 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1374 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1376 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1377 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1378 cdr.FirstSector = 0;
1380 else cdr.FirstSector = -1;
1385 Croc 2: $40 - only FORM1 (*)
1386 Judge Dredd: $C8 - only FORM1 (*)
1387 Sim Theme Park - no adpcm at all (zero)
1390 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1391 cdrReadInterruptSetResult(cdr.StatP);
1393 msfiAdd(cdr.SetSectorPlay, 1);
1395 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1404 bit 5 - 1 result ready
1406 bit 7 - 1 command being processed
1409 unsigned char cdrRead0(void) {
1410 if (cdr.ResultReady)
1415 cdr.Ctrl |= 0x40; // data fifo not empty
1417 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1420 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1422 return psxHu8(0x1800) = cdr.Ctrl;
1425 void cdrWrite0(unsigned char rt) {
1426 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1428 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1431 unsigned char cdrRead1(void) {
1432 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1433 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1437 if (cdr.ResultP == cdr.ResultC)
1438 cdr.ResultReady = 0;
1440 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1442 return psxHu8(0x1801);
1445 void cdrWrite1(unsigned char rt) {
1446 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1447 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1449 switch (cdr.Ctrl & 3) {
1453 cdr.AttenuatorRightToRightT = rt;
1459 #ifdef CDR_LOG_CMD_IRQ
1460 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1463 SysPrintf(" Param[%d] = {", cdr.ParamC);
1464 for (i = 0; i < cdr.ParamC; i++)
1465 SysPrintf(" %x,", cdr.Param[i]);
1472 cdr.ResultReady = 0;
1475 if (!cdr.CmdInProgress) {
1476 cdr.CmdInProgress = rt;
1477 // should be something like 12k + controller delays
1478 set_event(PSXINT_CDR, 5000);
1481 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1482 rt, cdr.Cmd, cdr.CmdInProgress);
1483 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1484 cdr.CmdInProgress = rt;
1490 unsigned char cdrRead2(void) {
1491 unsigned char ret = cdr.Transfer[0x920];
1493 if (cdr.FifoOffset < cdr.FifoSize)
1494 ret = cdr.Transfer[cdr.FifoOffset++];
1496 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1498 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1502 void cdrWrite2(unsigned char rt) {
1503 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1504 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1506 switch (cdr.Ctrl & 3) {
1508 if (cdr.ParamC < 8) // FIXME: size and wrapping
1509 cdr.Param[cdr.ParamC++] = rt;
1516 cdr.AttenuatorLeftToLeftT = rt;
1519 cdr.AttenuatorRightToLeftT = rt;
1524 unsigned char cdrRead3(void) {
1526 psxHu8(0x1803) = cdr.Stat | 0xE0;
1528 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1530 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1531 return psxHu8(0x1803);
1534 void cdrWrite3(unsigned char rt) {
1535 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1536 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1538 switch (cdr.Ctrl & 3) {
1542 if (cdr.Stat & rt) {
1543 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1544 + psxRegs.intCycle[PSXINT_CDR].cycle;
1545 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1546 #ifdef CDR_LOG_CMD_IRQ
1547 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1548 !!pending, cdr.CmdInProgress,
1549 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1551 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1552 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1555 if (cdr.CmdInProgress) {
1556 c = 2048 - (psxRegs.cycle - nextCycle);
1557 c = MAX_VALUE(c, 512);
1559 set_event(PSXINT_CDR, c);
1568 cdr.AttenuatorLeftToRightT = rt;
1572 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1573 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1574 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1575 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1581 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1582 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1584 else if (rt & 0x80) {
1585 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1586 case MODE_SIZE_2328:
1588 cdr.FifoOffset = 12;
1589 cdr.FifoSize = 2048 + 12;
1592 case MODE_SIZE_2340:
1595 cdr.FifoSize = 2340;
1599 else if (!(rt & 0xc0))
1600 cdr.FifoOffset = DATA_SIZE; // fifo empty
1603 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1604 u32 cdsize, max_words;
1609 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1610 if (cdr.FifoOffset == 0) {
1612 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1617 switch (chcr & 0x71000000) {
1619 ptr = getDmaRam(madr, &max_words);
1620 if (ptr == INVALID_PTR) {
1621 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1625 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1628 GS CDX: Enhancement CD crash
1631 - Spams DMA3 and gets buffer overrun
1633 size = DATA_SIZE - cdr.FifoOffset;
1636 if (size > max_words * 4)
1637 size = max_words * 4;
1640 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1641 cdr.FifoOffset += size;
1643 if (size < cdsize) {
1644 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1645 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1647 psxCpu->Clear(madr, cdsize / 4);
1649 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1651 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1653 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1654 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1658 psxRegs.cycle += (cdsize/4) * 24 - 20;
1663 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1667 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1671 void cdrDmaInterrupt(void)
1673 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1675 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1680 static void getCdInfo(void)
1684 CDR_getTN(cdr.ResultTN);
1685 CDR_getTD(0, cdr.SetSectorEnd);
1686 tmp = cdr.SetSectorEnd[0];
1687 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1688 cdr.SetSectorEnd[2] = tmp;
1692 memset(&cdr, 0, sizeof(cdr));
1698 cdr.FifoOffset = DATA_SIZE; // fifo empty
1700 CDR_getStatus(&stat);
1701 if (stat.Status & STATUS_SHELLOPEN) {
1702 cdr.DriveState = DRIVESTATE_LID_OPEN;
1703 cdr.StatP = STATUS_SHELLOPEN;
1705 else if (CdromId[0] == '\0') {
1706 cdr.DriveState = DRIVESTATE_STOPPED;
1710 cdr.DriveState = DRIVESTATE_STANDBY;
1711 cdr.StatP = STATUS_ROTATING;
1714 // BIOS player - default values
1715 cdr.AttenuatorLeftToLeft = 0x80;
1716 cdr.AttenuatorLeftToRight = 0x00;
1717 cdr.AttenuatorRightToLeft = 0x00;
1718 cdr.AttenuatorRightToRight = 0x80;
1723 int cdrFreeze(void *f, int Mode) {
1727 if (Mode == 0 && !Config.Cdda)
1730 cdr.freeze_ver = 0x63647202;
1731 gzfreeze(&cdr, sizeof(cdr));
1734 cdr.ParamP = cdr.ParamC;
1735 tmp = cdr.FifoOffset;
1738 gzfreeze(&tmp, sizeof(tmp));
1743 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1744 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1745 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1746 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1748 // read right sub data
1749 tmpp[0] = btoi(cdr.Prev[0]);
1750 tmpp[1] = btoi(cdr.Prev[1]);
1751 tmpp[2] = btoi(cdr.Prev[2]);
1756 if (cdr.freeze_ver < 0x63647202)
1757 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1759 Find_CurTrack(cdr.SetSectorPlay);
1761 CDR_play(cdr.SetSectorPlay);
1764 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1765 // old versions did not latch Reg2, have to fixup..
1766 if (cdr.Reg2 == 0) {
1767 SysPrintf("cdrom: fixing up old savestate\n");
1770 // also did not save Attenuator..
1771 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1772 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1774 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1782 void LidInterrupt(void) {
1784 cdrLidSeekInterrupt();