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( "CDDA STOP\n" );
522 // Magic the Gathering
523 // - looping territory cdda
526 //cdr.ResultReady = 1;
527 //cdr.Stat = DataReady;
529 setIrq(0x1000); // 0x1000 just for logging purposes
532 SetPlaySeekRead(cdr.StatP, 0);
534 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
535 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
537 cdr.Result[0] = cdr.StatP;
538 cdr.Result[1] = cdr.subq.Track;
539 cdr.Result[2] = cdr.subq.Index;
541 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
543 /* 8 is a hack. For accuracy, it should be 588. */
544 for (i = 0; i < 8; i++)
546 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
548 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
549 abs_lev_max |= abs_lev_chselect << 15;
551 if (cdr.subq.Absolute[2] & 0x10) {
552 cdr.Result[3] = cdr.subq.Relative[0];
553 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
554 cdr.Result[5] = cdr.subq.Relative[2];
557 cdr.Result[3] = cdr.subq.Absolute[0];
558 cdr.Result[4] = cdr.subq.Absolute[1];
559 cdr.Result[5] = cdr.subq.Absolute[2];
562 cdr.Result[6] = abs_lev_max >> 0;
563 cdr.Result[7] = abs_lev_max >> 8;
565 // Rayman: Logo freeze (resultready + dataready)
567 cdr.Stat = DataReady;
577 static int cdrSeekTime(unsigned char *target)
579 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
580 int seekTime = abs(diff) * (cdReadTime / 2000);
581 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
582 seekTime = MAX_VALUE(seekTime, 20000);
584 // need this stupidly long penalty or else Spyro2 intro desyncs
585 // note: if misapplied this breaks MGS cutscenes among other things
586 if (cyclesSinceRS > cdReadTime * 50)
587 seekTime += cdReadTime * 25;
588 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
589 // and then wants some slack time
590 else if (cyclesSinceRS < cdReadTime *3/2)
591 seekTime += cdReadTime;
593 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
594 CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
595 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime);
599 static u32 cdrAlignTimingHack(u32 cycles)
602 * timing hack for T'ai Fu - Wrath of the Tiger:
603 * The game has a bug where it issues some cdc commands from a low priority
604 * vint handler, however there is a higher priority default bios handler
605 * that acks the vint irq and returns, so game's handler is not reached
606 * (see bios irq handler chains at e004 and the game's irq handling func
607 * at 80036810). For the game to work, vint has to arrive after the bios
608 * vint handler rejects some other irq (of which only cd and rcnt2 are
609 * active), but before the game's handler loop reads I_STAT. The time
610 * window for this is quite small (~1k cycles of so). Apparently this
611 * somehow happens naturally on the real hardware.
613 * Note: always enforcing this breaks other games like Crash PAL version
614 * (inputs get dropped because bios handler doesn't see interrupts).
617 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
619 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
620 vint_rel += PSXCLK / 60;
621 while ((s32)(vint_rel - cycles) < 0)
622 vint_rel += PSXCLK / 60;
626 static void cdrUpdateTransferBuf(const u8 *buf);
627 static void cdrReadInterrupt(void);
628 static void cdrPrepCdda(s16 *buf, int samples);
629 static void cdrAttenuate(s16 *buf, int samples, int stereo);
631 static void msfiAdd(u8 *msfi, u32 count)
645 static void msfiSub(u8 *msfi, u32 count)
649 if ((s8)msfi[2] < 0) {
652 if ((s8)msfi[1] < 0) {
659 void cdrPlayReadInterrupt(void)
661 cdr.LastReadSeekCycles = psxRegs.cycle;
668 if (!cdr.Play) return;
670 CDR_LOG( "CDDA - %d:%d:%d\n",
671 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
673 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
674 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
676 SetPlaySeekRead(cdr.StatP, 0);
677 cdr.TrackChanged = TRUE;
680 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
683 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
684 cdrPlayInterrupt_Autopause();
686 if (!cdr.Muted && cdr.Play && !Config.Cdda) {
687 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
688 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
689 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
693 msfiAdd(cdr.SetSectorPlay, 1);
695 // update for CdlGetlocP/autopause
696 generate_subq(cdr.SetSectorPlay);
698 CDRPLAYREAD_INT(cdReadTime, 0);
701 #define CMD_PART2 0x100
702 #define CMD_WHILE_NOT_READY 0x200
704 void cdrInterrupt(void) {
705 int start_rotating = 0;
707 u32 cycles, seekTime = 0;
708 u32 second_resp_time = 0;
718 CDR_LOG_I("cmd %02x with irqstat %x\n",
719 cdr.CmdInProgress, cdr.Stat);
722 if (cdr.Irq1Pending) {
723 // hand out the "newest" sector, according to nocash
724 cdrUpdateTransferBuf(CDR_getBuffer());
725 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
726 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
727 cdr.CmdInProgress, cdr.Irq1Pending);
729 cdr.Result[0] = cdr.Irq1Pending;
730 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
738 cdr.Result[0] = cdr.StatP;
739 cdr.Stat = Acknowledge;
741 Cmd = cdr.CmdInProgress;
742 cdr.CmdInProgress = 0;
751 switch (cdr.DriveState) {
752 case DRIVESTATE_PREPARE_CD:
754 // Syphon filter 2 expects commands to work shortly after it sees
755 // STATUS_ROTATING, so give up trying to emulate the startup seq
756 cdr.DriveState = DRIVESTATE_STANDBY;
757 cdr.StatP &= ~STATUS_SEEK;
758 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
762 case DRIVESTATE_LID_OPEN:
763 case DRIVESTATE_RESCAN_CD:
764 // no disk or busy with the initial scan, allowed cmds are limited
765 not_ready = CMD_WHILE_NOT_READY;
769 switch (Cmd | not_ready) {
771 case CdlNop + CMD_WHILE_NOT_READY:
772 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
773 cdr.StatP &= ~STATUS_SHELLOPEN;
777 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
778 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
780 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
781 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))
783 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
784 if (++cdr.errorRetryhack > 100)
786 error = ERROR_INVALIDARG;
791 for (i = 0; i < 3; i++)
792 set_loc[i] = btoi(cdr.Param[i]);
793 memcpy(cdr.SetSector, set_loc, 3);
794 cdr.SetSector[3] = 0;
795 cdr.SetlocPending = 1;
796 cdr.errorRetryhack = 0;
805 cdr.FastBackward = 0;
809 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
811 if (ParamC != 0 && cdr.Param[0] != 0) {
812 int track = btoi( cdr.Param[0] );
814 if (track <= cdr.ResultTN[1])
815 cdr.CurTrack = track;
817 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
819 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
820 for (i = 0; i < 3; i++)
821 set_loc[i] = cdr.ResultTD[2 - i];
822 seekTime = cdrSeekTime(set_loc);
823 memcpy(cdr.SetSectorPlay, set_loc, 3);
826 else if (cdr.SetlocPending) {
827 seekTime = cdrSeekTime(cdr.SetSector);
828 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
831 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
832 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
834 cdr.SetlocPending = 0;
837 Rayman: detect track changes
840 Twisted Metal 2: skip PREGAP + starting accurate SubQ
841 - plays tracks without retry play
843 Wild 9: skip PREGAP + starting accurate SubQ
844 - plays tracks without retry play
846 Find_CurTrack(cdr.SetSectorPlay);
847 generate_subq(cdr.SetSectorPlay);
848 cdr.LocL[0] = LOCL_INVALID;
849 cdr.SubqForwardSectors = 1;
850 cdr.TrackChanged = FALSE;
852 cdr.ReportDelay = 60;
856 CDR_play(cdr.SetSectorPlay);
858 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
860 // BIOS player - set flag again
863 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
868 // TODO: error 80 if stopped
871 // GameShark CD Player: Calls 2x + Play 2x
873 cdr.FastBackward = 0;
879 // GameShark CD Player: Calls 2x + Play 2x
880 cdr.FastBackward = 1;
885 if (cdr.DriveState != DRIVESTATE_STOPPED) {
886 error = ERROR_INVALIDARG;
889 second_resp_time = cdReadTime * 125 / 2;
893 case CdlStandby + CMD_PART2:
899 // grab time for current track
900 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
902 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
903 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
904 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
909 SetPlaySeekRead(cdr.StatP, 0);
910 cdr.StatP &= ~STATUS_ROTATING;
911 cdr.LocL[0] = LOCL_INVALID;
913 second_resp_time = 0x800;
914 if (cdr.DriveState == DRIVESTATE_STANDBY)
915 second_resp_time = cdReadTime * 30 / 2;
917 cdr.DriveState = DRIVESTATE_STOPPED;
920 case CdlStop + CMD_PART2:
928 // how the drive maintains the position while paused is quite
929 // complicated, this is the minimum to make "Bedlam" happy
930 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
934 Gundam Battle Assault 2: much slower (*)
935 - Fixes boot, gameplay
937 Hokuto no Ken 2: slower
938 - Fixes intro + subtitles
940 InuYasha - Feudal Fairy Tale: slower
943 /* Gameblabla - Tightening the timings (as taken from Duckstation).
944 * The timings from Duckstation are based upon hardware tests.
945 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
946 * seems to be timing sensitive as it can depend on the CPU's clock speed.
948 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
950 second_resp_time = 7000;
954 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
956 SetPlaySeekRead(cdr.StatP, 0);
959 case CdlPause + CMD_PART2:
964 case CdlReset + CMD_WHILE_NOT_READY:
967 SetPlaySeekRead(cdr.StatP, 0);
968 cdr.LocL[0] = LOCL_INVALID;
970 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
971 second_resp_time = not_ready ? 70000 : 4100000;
975 case CdlReset + CMD_PART2:
976 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
989 cdr.File = cdr.Param[0];
990 cdr.Channel = cdr.Param[1];
994 case CdlSetmode + CMD_WHILE_NOT_READY:
995 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
996 cdr.Mode = cdr.Param[0];
1000 case CdlGetparam + CMD_WHILE_NOT_READY:
1001 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1003 cdr.Result[1] = cdr.Mode;
1005 cdr.Result[3] = cdr.File;
1006 cdr.Result[4] = cdr.Channel;
1010 if (cdr.LocL[0] == LOCL_INVALID) {
1015 memcpy(cdr.Result, cdr.LocL, 8);
1020 memcpy(&cdr.Result, &cdr.subq, 8);
1023 case CdlReadT: // SetSession?
1025 second_resp_time = cdReadTime * 290 / 4;
1029 case CdlReadT + CMD_PART2:
1030 cdr.Stat = Complete;
1035 if (CDR_getTN(cdr.ResultTN) == -1) {
1036 cdr.Stat = DiskError;
1037 cdr.Result[0] |= STATUS_ERROR;
1039 cdr.Stat = Acknowledge;
1040 cdr.Result[1] = itob(cdr.ResultTN[0]);
1041 cdr.Result[2] = itob(cdr.ResultTN[1]);
1046 cdr.Track = btoi(cdr.Param[0]);
1048 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1049 cdr.Stat = DiskError;
1050 cdr.Result[0] |= STATUS_ERROR;
1052 cdr.Stat = Acknowledge;
1053 cdr.Result[0] = cdr.StatP;
1054 cdr.Result[1] = itob(cdr.ResultTD[2]);
1055 cdr.Result[2] = itob(cdr.ResultTD[1]);
1056 /* According to Nocash's documentation, the function doesn't care about ff.
1057 * This can be seen also in Mednafen's implementation. */
1058 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1066 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1068 seekTime = cdrSeekTime(cdr.SetSector);
1069 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1071 Crusaders of Might and Magic = 0.5x-4x
1072 - fix cutscene speech start
1074 Eggs of Steel = 2x-?
1078 - fix cutscene speech
1083 second_resp_time = cdReadTime + seekTime;
1087 case CdlSeekL + CMD_PART2:
1088 case CdlSeekP + CMD_PART2:
1089 SetPlaySeekRead(cdr.StatP, 0);
1090 cdr.Result[0] = cdr.StatP;
1091 cdr.Stat = Complete;
1093 Find_CurTrack(cdr.SetSectorPlay);
1094 read_ok = ReadTrack(cdr.SetSectorPlay);
1095 if (read_ok && (buf = CDR_getBuffer()))
1096 memcpy(cdr.LocL, buf, 8);
1097 UpdateSubq(cdr.SetSectorPlay);
1098 cdr.TrackChanged = FALSE;
1099 cdr.LastReadSeekCycles = psxRegs.cycle;
1103 case CdlTest + CMD_WHILE_NOT_READY:
1104 switch (cdr.Param[0]) {
1105 case 0x20: // System Controller ROM Version
1107 memcpy(cdr.Result, Test20, 4);
1111 memcpy(cdr.Result, Test22, 4);
1113 case 0x23: case 0x24:
1115 memcpy(cdr.Result, Test23, 4);
1121 second_resp_time = 20480;
1124 case CdlID + CMD_PART2:
1126 cdr.Result[0] = cdr.StatP;
1131 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1132 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1133 cdr.Result[1] = 0xc0;
1137 cdr.Result[1] |= 0x10;
1138 if (CdromId[0] == '\0')
1139 cdr.Result[1] |= 0x80;
1141 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1143 /* This adds the string "PCSX" in Playstation bios boot screen */
1144 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1145 cdr.Stat = Complete;
1149 case CdlInit + CMD_WHILE_NOT_READY:
1152 SetPlaySeekRead(cdr.StatP, 0);
1153 // yes, it really sets STATUS_SHELLOPEN
1154 cdr.StatP |= STATUS_SHELLOPEN;
1155 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1156 set_event(PSXINT_CDRLID, 20480);
1161 case CdlGetQ + CMD_WHILE_NOT_READY:
1165 case CdlReadToc + CMD_WHILE_NOT_READY:
1166 cdr.LocL[0] = LOCL_INVALID;
1167 second_resp_time = cdReadTime * 180 / 4;
1171 case CdlReadToc + CMD_PART2:
1172 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1173 cdr.Stat = Complete;
1178 if (cdr.Reading && !cdr.SetlocPending)
1181 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1183 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1184 // Read* acts as play for cdda tracks in cdda mode
1188 if (cdr.SetlocPending) {
1189 seekTime = cdrSeekTime(cdr.SetSector);
1190 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1191 cdr.SetlocPending = 0;
1194 cdr.FirstSector = 1;
1196 // Fighting Force 2 - update subq time immediately
1198 UpdateSubq(cdr.SetSectorPlay);
1199 cdr.LocL[0] = LOCL_INVALID;
1200 cdr.SubqForwardSectors = 1;
1201 cdr.sectorsRead = 0;
1203 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1205 if (Config.hacks.cdr_read_timing)
1206 cycles = cdrAlignTimingHack(cycles);
1207 CDRPLAYREAD_INT(cycles, 1);
1209 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1215 error = ERROR_INVALIDCMD;
1220 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1221 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1222 cdr.Stat = DiskError;
1223 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1227 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1228 cdr.DriveState = DRIVESTATE_STANDBY;
1229 cdr.StatP |= STATUS_ROTATING;
1232 if (second_resp_time) {
1233 cdr.CmdInProgress = Cmd | 0x100;
1234 set_event(PSXINT_CDR, second_resp_time);
1236 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1237 cdr.CmdInProgress = cdr.Cmd;
1238 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1245 #define ssat32_to_16(v) \
1246 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1248 #define ssat32_to_16(v) do { \
1249 if (v < -32768) v = -32768; \
1250 else if (v > 32767) v = 32767; \
1254 static void cdrPrepCdda(s16 *buf, int samples)
1256 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1258 for (i = 0; i < samples; i++) {
1259 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1260 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1265 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1268 int ll = cdr.AttenuatorLeftToLeft;
1269 int lr = cdr.AttenuatorLeftToRight;
1270 int rl = cdr.AttenuatorRightToLeft;
1271 int rr = cdr.AttenuatorRightToRight;
1273 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1276 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1280 for (i = 0; i < samples; i++) {
1283 l = (l * ll + r * rl) >> 7;
1284 r = (r * rr + l * lr) >> 7;
1292 for (i = 0; i < samples; i++) {
1294 l = l * (ll + rl) >> 7;
1295 //r = r * (rr + lr) >> 7;
1303 static void cdrReadInterruptSetResult(unsigned char result)
1306 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1307 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1308 cdr.CmdInProgress, cdr.Stat);
1309 cdr.Irq1Pending = result;
1313 cdr.Result[0] = result;
1314 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1318 static void cdrUpdateTransferBuf(const u8 *buf)
1322 memcpy(cdr.Transfer, buf, DATA_SIZE);
1323 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1324 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1325 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1326 if (cdr.FifoOffset < 2048 + 12)
1327 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1330 static void cdrReadInterrupt(void)
1332 u8 *buf = NULL, *hdr;
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.Stat && !cdr.Irq1Pending)
1363 cdrUpdateTransferBuf(buf);
1365 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1367 // Firemen 2: Multi-XA files - briefings, cutscenes
1368 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1370 cdr.Channel = hdr[1];
1374 * Skips playing on channel 255.
1375 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1376 * TODO : Check if this is the proper behaviour.
1378 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1379 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1381 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1382 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1383 cdr.FirstSector = 0;
1385 else cdr.FirstSector = -1;
1390 Croc 2: $40 - only FORM1 (*)
1391 Judge Dredd: $C8 - only FORM1 (*)
1392 Sim Theme Park - no adpcm at all (zero)
1395 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1396 cdrReadInterruptSetResult(cdr.StatP);
1398 msfiAdd(cdr.SetSectorPlay, 1);
1400 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1409 bit 5 - 1 result ready
1411 bit 7 - 1 command being processed
1414 unsigned char cdrRead0(void) {
1415 if (cdr.ResultReady)
1420 cdr.Ctrl |= 0x40; // data fifo not empty
1422 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1425 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1427 return psxHu8(0x1800) = cdr.Ctrl;
1430 void cdrWrite0(unsigned char rt) {
1431 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1433 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1436 unsigned char cdrRead1(void) {
1437 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1438 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1442 if (cdr.ResultP == cdr.ResultC)
1443 cdr.ResultReady = 0;
1445 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1447 return psxHu8(0x1801);
1450 void cdrWrite1(unsigned char rt) {
1451 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1452 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1454 switch (cdr.Ctrl & 3) {
1458 cdr.AttenuatorRightToRightT = rt;
1464 #ifdef CDR_LOG_CMD_IRQ
1465 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1468 SysPrintf(" Param[%d] = {", cdr.ParamC);
1469 for (i = 0; i < cdr.ParamC; i++)
1470 SysPrintf(" %x,", cdr.Param[i]);
1477 cdr.ResultReady = 0;
1480 if (!cdr.CmdInProgress) {
1481 cdr.CmdInProgress = rt;
1482 // should be something like 12k + controller delays
1483 set_event(PSXINT_CDR, 5000);
1486 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1487 rt, cdr.Cmd, cdr.CmdInProgress);
1488 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1489 cdr.CmdInProgress = rt;
1495 unsigned char cdrRead2(void) {
1496 unsigned char ret = cdr.Transfer[0x920];
1498 if (cdr.FifoOffset < cdr.FifoSize)
1499 ret = cdr.Transfer[cdr.FifoOffset++];
1501 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1503 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1507 void cdrWrite2(unsigned char rt) {
1508 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1509 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1511 switch (cdr.Ctrl & 3) {
1513 if (cdr.ParamC < 8) // FIXME: size and wrapping
1514 cdr.Param[cdr.ParamC++] = rt;
1521 cdr.AttenuatorLeftToLeftT = rt;
1524 cdr.AttenuatorRightToLeftT = rt;
1529 unsigned char cdrRead3(void) {
1531 psxHu8(0x1803) = cdr.Stat | 0xE0;
1533 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1535 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1536 return psxHu8(0x1803);
1539 void cdrWrite3(unsigned char rt) {
1540 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1541 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1543 switch (cdr.Ctrl & 3) {
1547 if (cdr.Stat & rt) {
1548 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1549 + psxRegs.intCycle[PSXINT_CDR].cycle;
1550 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1551 #ifdef CDR_LOG_CMD_IRQ
1552 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1553 !!pending, cdr.CmdInProgress,
1554 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1556 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1557 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1560 if (cdr.CmdInProgress) {
1561 c = 2048 - (psxRegs.cycle - nextCycle);
1562 c = MAX_VALUE(c, 512);
1564 set_event(PSXINT_CDR, c);
1573 cdr.AttenuatorLeftToRightT = rt;
1577 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1578 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1579 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1580 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1586 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1587 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1589 else if (rt & 0x80) {
1590 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1591 case MODE_SIZE_2328:
1593 cdr.FifoOffset = 12;
1594 cdr.FifoSize = 2048 + 12;
1597 case MODE_SIZE_2340:
1600 cdr.FifoSize = 2340;
1604 else if (!(rt & 0xc0))
1605 cdr.FifoOffset = DATA_SIZE; // fifo empty
1608 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1609 u32 cdsize, max_words;
1614 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1615 if (cdr.FifoOffset == 0) {
1617 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1622 switch (chcr & 0x71000000) {
1624 ptr = getDmaRam(madr, &max_words);
1625 if (ptr == INVALID_PTR) {
1626 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1630 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1633 GS CDX: Enhancement CD crash
1636 - Spams DMA3 and gets buffer overrun
1638 size = DATA_SIZE - cdr.FifoOffset;
1641 if (size > max_words * 4)
1642 size = max_words * 4;
1645 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1646 cdr.FifoOffset += size;
1648 if (size < cdsize) {
1649 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1650 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1652 psxCpu->Clear(madr, cdsize / 4);
1654 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1656 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1658 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1659 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1663 psxRegs.cycle += (cdsize/4) * 24 - 20;
1668 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1672 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1676 void cdrDmaInterrupt(void)
1678 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1680 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1685 static void getCdInfo(void)
1689 CDR_getTN(cdr.ResultTN);
1690 CDR_getTD(0, cdr.SetSectorEnd);
1691 tmp = cdr.SetSectorEnd[0];
1692 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1693 cdr.SetSectorEnd[2] = tmp;
1697 memset(&cdr, 0, sizeof(cdr));
1703 cdr.FifoOffset = DATA_SIZE; // fifo empty
1704 if (CdromId[0] == '\0') {
1705 cdr.DriveState = DRIVESTATE_STOPPED;
1709 cdr.DriveState = DRIVESTATE_STANDBY;
1710 cdr.StatP = STATUS_ROTATING;
1713 // BIOS player - default values
1714 cdr.AttenuatorLeftToLeft = 0x80;
1715 cdr.AttenuatorLeftToRight = 0x00;
1716 cdr.AttenuatorRightToLeft = 0x00;
1717 cdr.AttenuatorRightToRight = 0x80;
1722 int cdrFreeze(void *f, int Mode) {
1726 if (Mode == 0 && !Config.Cdda)
1729 cdr.freeze_ver = 0x63647202;
1730 gzfreeze(&cdr, sizeof(cdr));
1733 cdr.ParamP = cdr.ParamC;
1734 tmp = cdr.FifoOffset;
1737 gzfreeze(&tmp, sizeof(tmp));
1742 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1743 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1744 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1745 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1747 // read right sub data
1748 tmpp[0] = btoi(cdr.Prev[0]);
1749 tmpp[1] = btoi(cdr.Prev[1]);
1750 tmpp[2] = btoi(cdr.Prev[2]);
1755 if (cdr.freeze_ver < 0x63647202)
1756 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1758 Find_CurTrack(cdr.SetSectorPlay);
1760 CDR_play(cdr.SetSectorPlay);
1763 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1764 // old versions did not latch Reg2, have to fixup..
1765 if (cdr.Reg2 == 0) {
1766 SysPrintf("cdrom: fixing up old savestate\n");
1769 // also did not save Attenuator..
1770 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1771 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1773 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1781 void LidInterrupt(void) {
1783 cdrLidSeekInterrupt();