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.
28 #include "arm_features.h"
32 #define CDR_LOG SysPrintf
37 #define CDR_LOG_I SysPrintf
39 #define CDR_LOG_I log_unhandled
42 #define CDR_LOG_IO SysPrintf
44 #define CDR_LOG_IO(...)
46 //#define CDR_LOG_CMD_IRQ
49 // unused members maintain savesate compatibility
50 unsigned char unused0;
51 unsigned char unused1;
53 unsigned char unused2;
59 unsigned char Transfer[DATA_SIZE];
63 unsigned char Relative[3];
64 unsigned char Absolute[3];
66 unsigned char TrackChanged;
67 unsigned char unused3[3];
68 unsigned int freeze_ver;
70 unsigned char Prev[4];
71 unsigned char Param[8];
72 unsigned char Result[16];
76 unsigned char ResultC;
77 unsigned char ResultP;
78 unsigned char ResultReady;
80 unsigned char SubqForwardSectors;
81 unsigned char SetlocPending;
84 unsigned char ResultTN[6];
85 unsigned char ResultTD[4];
86 unsigned char SetSectorPlay[4];
87 unsigned char SetSectorEnd[4];
88 unsigned char SetSector[4];
92 int Mode, File, Channel;
93 unsigned char LocL[8];
113 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
114 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
115 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
116 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
118 static s16 read_buf[CD_FRAMESIZE_RAW/2];
120 /* CD-ROM magic numbers */
121 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
126 #define CdlBackward 5
134 #define CdlSetfilter 13
135 #define CdlSetmode 14
136 #define CdlGetparam 15
137 #define CdlGetlocL 16
138 #define CdlGetlocP 17
144 #define CdlSetclock 23
145 #define CdlGetclock 24
151 #define CdlReadToc 30
153 #ifdef CDR_LOG_CMD_IRQ
154 static const char * const CmdName[0x100] = {
155 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
156 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
157 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
158 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
159 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
160 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
161 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
162 "CdlInit", NULL, "CDlReadToc", NULL
166 unsigned char Test04[] = { 0 };
167 unsigned char Test05[] = { 0 };
168 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
169 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
170 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
176 #define Acknowledge 3
181 #define MODE_SPEED (1<<7) // 0x80
182 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
183 #define MODE_SIZE_2340 (1<<5) // 0x20
184 #define MODE_SIZE_2328 (1<<4) // 0x10
185 #define MODE_SIZE_2048 (0<<4) // 0x00
186 #define MODE_SF (1<<3) // 0x08 channel on/off
187 #define MODE_REPORT (1<<2) // 0x04
188 #define MODE_AUTOPAUSE (1<<1) // 0x02
189 #define MODE_CDDA (1<<0) // 0x01
192 #define STATUS_PLAY (1<<7) // 0x80
193 #define STATUS_SEEK (1<<6) // 0x40
194 #define STATUS_READ (1<<5) // 0x20
195 #define STATUS_SHELLOPEN (1<<4) // 0x10
196 #define STATUS_UNKNOWN3 (1<<3) // 0x08
197 #define STATUS_UNKNOWN2 (1<<2) // 0x04
198 #define STATUS_ROTATING (1<<1) // 0x02
199 #define STATUS_ERROR (1<<0) // 0x01
202 #define ERROR_NOTREADY (1<<7) // 0x80
203 #define ERROR_INVALIDCMD (1<<6) // 0x40
204 #define ERROR_INVALIDARG (1<<5) // 0x20
206 // 1x = 75 sectors per second
207 // PSXCLK = 1 sec in the ps
208 // so (PSXCLK / 75) = cdr read time (linuzappz)
209 #define cdReadTime (PSXCLK / 75)
211 #define LOCL_INVALID 0xff
212 #define SUBQ_FORWARD_SECTORS 2u
215 DRIVESTATE_STANDBY = 0, // pause, play, read
217 DRIVESTATE_RESCAN_CD,
218 DRIVESTATE_PREPARE_CD,
222 static struct CdrStat stat;
224 static unsigned int msf2sec(const u8 *msf) {
225 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
228 // for that weird psemu API..
229 static unsigned int fsm2sec(const u8 *msf) {
230 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
233 static void sec2msf(unsigned int s, u8 *msf) {
234 msf[0] = s / 75 / 60;
235 s = s - msf[0] * 75 * 60;
242 #define CDR_INT(eCycle) { \
243 psxRegs.interrupt |= (1 << PSXINT_CDR); \
244 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
245 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
246 new_dyna_set_event(PSXINT_CDR, eCycle); \
249 // cdrPlayReadInterrupt
250 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
252 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
254 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
256 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
257 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
258 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
261 // cdrLidSeekInterrupt
262 #define CDRLID_INT(eCycle) { \
263 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
264 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
265 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
266 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
269 #define StopReading() { \
271 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
274 #define StopCdda() { \
275 if (cdr.Play && !Config.Cdda) CDR_stop(); \
277 cdr.FastForward = 0; \
278 cdr.FastBackward = 0; \
281 #define SetPlaySeekRead(x, f) { \
282 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
286 #define SetResultSize(size) { \
288 cdr.ResultC = size; \
289 cdr.ResultReady = 1; \
292 static void setIrq(int log_cmd)
294 if (cdr.Stat & cdr.Reg2)
295 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
297 #ifdef CDR_LOG_CMD_IRQ
301 SysPrintf("%u cdrom: CDR IRQ=%d cmd %02x stat %02x: ",
302 psxRegs.cycle, !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
303 for (i = 0; i < cdr.ResultC; i++)
304 SysPrintf("%02x ", cdr.Result[i]);
310 // timing used in this function was taken from tests on real hardware
311 // (yes it's slow, but you probably don't want to modify it)
312 void cdrLidSeekInterrupt(void)
314 CDR_LOG_I("%u %s cdr.DriveState=%d\n", psxRegs.cycle, __func__, cdr.DriveState);
316 switch (cdr.DriveState) {
318 case DRIVESTATE_STANDBY:
321 SetPlaySeekRead(cdr.StatP, 0);
323 if (CDR_getStatus(&stat) == -1)
326 if (stat.Status & STATUS_SHELLOPEN)
328 cdr.DriveState = DRIVESTATE_LID_OPEN;
333 case DRIVESTATE_LID_OPEN:
334 if (CDR_getStatus(&stat) == -1)
335 stat.Status &= ~STATUS_SHELLOPEN;
338 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
339 cdr.StatP |= STATUS_SHELLOPEN;
341 // could generate error irq here, but real hardware
342 // only sometimes does that
343 // (not done when lots of commands are sent?)
345 CDRLID_INT(cdReadTime * 30);
348 else if (cdr.StatP & STATUS_ROTATING) {
349 cdr.StatP &= ~STATUS_ROTATING;
351 else if (!(stat.Status & STATUS_SHELLOPEN)) {
355 // cdr.StatP STATUS_SHELLOPEN is "sticky"
356 // and is only cleared by CdlNop
358 cdr.DriveState = DRIVESTATE_RESCAN_CD;
359 CDRLID_INT(cdReadTime * 105);
364 CDRLID_INT(cdReadTime * 3);
367 case DRIVESTATE_RESCAN_CD:
368 cdr.StatP |= STATUS_ROTATING;
369 cdr.DriveState = DRIVESTATE_PREPARE_CD;
371 // this is very long on real hardware, over 6 seconds
372 // make it a bit faster here...
373 CDRLID_INT(cdReadTime * 150);
376 case DRIVESTATE_PREPARE_CD:
377 if (cdr.StatP & STATUS_SEEK) {
378 SetPlaySeekRead(cdr.StatP, 0);
379 cdr.DriveState = DRIVESTATE_STANDBY;
382 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
383 CDRLID_INT(cdReadTime * 26);
389 static void Find_CurTrack(const u8 *time)
393 current = msf2sec(time);
395 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
396 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
397 sect = fsm2sec(cdr.ResultTD);
398 if (sect - current >= 150)
403 static void generate_subq(const u8 *time)
405 unsigned char start[3], next[3];
406 unsigned int this_s, start_s, next_s, pregap;
409 CDR_getTD(cdr.CurTrack, start);
410 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
412 CDR_getTD(cdr.CurTrack + 1, next);
415 // last track - cd size
417 next[0] = cdr.SetSectorEnd[2];
418 next[1] = cdr.SetSectorEnd[1];
419 next[2] = cdr.SetSectorEnd[0];
422 this_s = msf2sec(time);
423 start_s = fsm2sec(start);
424 next_s = fsm2sec(next);
426 cdr.TrackChanged = FALSE;
428 if (next_s - this_s < pregap) {
429 cdr.TrackChanged = TRUE;
436 relative_s = this_s - start_s;
437 if (relative_s < 0) {
439 relative_s = -relative_s;
441 sec2msf(relative_s, cdr.subq.Relative);
443 cdr.subq.Track = itob(cdr.CurTrack);
444 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
445 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
446 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
447 cdr.subq.Absolute[0] = itob(time[0]);
448 cdr.subq.Absolute[1] = itob(time[1]);
449 cdr.subq.Absolute[2] = itob(time[2]);
452 static int ReadTrack(const u8 *time)
454 unsigned char tmp[3];
457 tmp[0] = itob(time[0]);
458 tmp[1] = itob(time[1]);
459 tmp[2] = itob(time[2]);
461 if (memcmp(cdr.Prev, tmp, 3) == 0)
464 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
466 read_ok = CDR_readTrack(tmp);
468 memcpy(cdr.Prev, tmp, 3);
472 static void UpdateSubq(const u8 *time)
474 const struct SubQ *subq;
480 subq = (struct SubQ *)CDR_getBufferSub(MSF2SECT(time[0], time[1], time[2]));
481 if (subq != NULL && cdr.CurTrack == 1) {
482 crc = calcCrc((u8 *)subq + 12, 10);
483 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
484 cdr.subq.Track = subq->TrackNumber;
485 cdr.subq.Index = subq->IndexNumber;
486 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
487 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
490 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
491 time[0], time[1], time[2]);
498 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
499 cdr.subq.Track, cdr.subq.Index,
500 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
501 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
504 static void cdrPlayInterrupt_Autopause()
507 boolean abs_lev_chselect;
510 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
511 CDR_LOG( "CDDA STOP\n" );
513 // Magic the Gathering
514 // - looping territory cdda
517 //cdr.ResultReady = 1;
518 //cdr.Stat = DataReady;
520 setIrq(0x1000); // 0x1000 just for logging purposes
523 SetPlaySeekRead(cdr.StatP, 0);
525 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
526 cdr.Result[0] = cdr.StatP;
527 cdr.Result[1] = cdr.subq.Track;
528 cdr.Result[2] = cdr.subq.Index;
530 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
532 /* 8 is a hack. For accuracy, it should be 588. */
533 for (i = 0; i < 8; i++)
535 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
537 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
538 abs_lev_max |= abs_lev_chselect << 15;
540 if (cdr.subq.Absolute[2] & 0x10) {
541 cdr.Result[3] = cdr.subq.Relative[0];
542 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
543 cdr.Result[5] = cdr.subq.Relative[2];
546 cdr.Result[3] = cdr.subq.Absolute[0];
547 cdr.Result[4] = cdr.subq.Absolute[1];
548 cdr.Result[5] = cdr.subq.Absolute[2];
551 cdr.Result[6] = abs_lev_max >> 0;
552 cdr.Result[7] = abs_lev_max >> 8;
554 // Rayman: Logo freeze (resultready + dataready)
556 cdr.Stat = DataReady;
563 static int cdrSeekTime(unsigned char *target)
565 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
566 int seekTime = abs(diff) * (cdReadTime / 200);
569 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
570 * and was unreliable for that game.
571 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
573 * Obviously, this isn't perfect but right now, it should be a bit better.
574 * Games to test this against if you change that setting :
575 * - Driver (titlescreen music delay and retry mission)
576 * - Worms Pinball (Will either not boot or crash in the memory card screen)
577 * - Viewpoint (short pauses if the delay in the ingame music is too long)
579 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
580 * However, 1000000 is not enough for Worms Pinball to reliably boot.
582 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
583 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
587 static u32 cdrAlignTimingHack(u32 cycles)
590 * timing hack for T'ai Fu - Wrath of the Tiger:
591 * The game has a bug where it issues some cdc commands from a low priority
592 * vint handler, however there is a higher priority default bios handler
593 * that acks the vint irq and returns, so game's handler is not reached
594 * (see bios irq handler chains at e004 and the game's irq handling func
595 * at 80036810). For the game to work, vint has to arrive after the bios
596 * vint handler rejects some other irq (of which only cd and rcnt2 are
597 * active), but before the game's handler loop reads I_STAT. The time
598 * window for this is quite small (~1k cycles of so). Apparently this
599 * somehow happens naturally on the real hardware.
601 * Note: always enforcing this breaks other games like Crash PAL version
602 * (inputs get dropped because bios handler doesn't see interrupts).
605 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
607 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
608 vint_rel += PSXCLK / 60;
609 while ((s32)(vint_rel - cycles) < 0)
610 vint_rel += PSXCLK / 60;
614 static void cdrUpdateTransferBuf(const u8 *buf);
615 static void cdrReadInterrupt(void);
616 static void cdrPrepCdda(s16 *buf, int samples);
617 static void cdrAttenuate(s16 *buf, int samples, int stereo);
619 static void msfiAdd(u8 *msfi, u32 count)
633 void cdrPlayReadInterrupt(void)
640 if (!cdr.Play) return;
642 CDR_LOG( "CDDA - %d:%d:%d\n",
643 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
645 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
646 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
648 SetPlaySeekRead(cdr.StatP, 0);
649 cdr.TrackChanged = TRUE;
652 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
655 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
656 cdrPlayInterrupt_Autopause();
658 if (!cdr.Muted && !Config.Cdda) {
659 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
660 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
661 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
665 msfiAdd(cdr.SetSectorPlay, 1);
667 // update for CdlGetlocP/autopause
668 generate_subq(cdr.SetSectorPlay);
670 CDRPLAYREAD_INT(cdReadTime, 0);
673 #define CMD_PART2 0x100
674 #define CMD_WHILE_NOT_READY 0x200
676 void cdrInterrupt(void) {
677 int start_rotating = 0;
679 u32 cycles, seekTime = 0;
680 u32 second_resp_time = 0;
690 CDR_LOG_I("%u cdrom: cmd %02x with irqstat %x\n",
691 psxRegs.cycle, cdr.CmdInProgress, cdr.Stat);
694 if (cdr.Irq1Pending) {
695 // hand out the "newest" sector, according to nocash
696 cdrUpdateTransferBuf(CDR_getBuffer());
697 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
698 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
700 cdr.Result[0] = cdr.Irq1Pending;
701 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
709 cdr.Result[0] = cdr.StatP;
710 cdr.Stat = Acknowledge;
712 Cmd = cdr.CmdInProgress;
713 cdr.CmdInProgress = 0;
722 switch (cdr.DriveState) {
723 case DRIVESTATE_PREPARE_CD:
725 // Syphon filter 2 expects commands to work shortly after it sees
726 // STATUS_ROTATING, so give up trying to emulate the startup seq
727 cdr.DriveState = DRIVESTATE_STANDBY;
728 cdr.StatP &= ~STATUS_SEEK;
729 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
733 case DRIVESTATE_LID_OPEN:
734 case DRIVESTATE_RESCAN_CD:
735 // no disk or busy with the initial scan, allowed cmds are limited
736 not_ready = CMD_WHILE_NOT_READY;
740 switch (Cmd | not_ready) {
742 case CdlNop + CMD_WHILE_NOT_READY:
743 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
744 cdr.StatP &= ~STATUS_SHELLOPEN;
748 case CdlSetloc + CMD_WHILE_NOT_READY:
749 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
751 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
752 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))
754 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
755 error = ERROR_INVALIDARG;
760 for (i = 0; i < 3; i++)
761 set_loc[i] = btoi(cdr.Param[i]);
762 memcpy(cdr.SetSector, set_loc, 3);
763 cdr.SetSector[3] = 0;
764 cdr.SetlocPending = 1;
773 cdr.FastBackward = 0;
777 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
779 if (ParamC != 0 && cdr.Param[0] != 0) {
780 int track = btoi( cdr.Param[0] );
782 if (track <= cdr.ResultTN[1])
783 cdr.CurTrack = track;
785 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
787 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
788 for (i = 0; i < 3; i++)
789 set_loc[i] = cdr.ResultTD[2 - i];
790 seekTime = cdrSeekTime(set_loc);
791 memcpy(cdr.SetSectorPlay, set_loc, 3);
794 else if (cdr.SetlocPending) {
795 seekTime = cdrSeekTime(cdr.SetSector);
796 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
799 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
800 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
802 cdr.SetlocPending = 0;
805 Rayman: detect track changes
808 Twisted Metal 2: skip PREGAP + starting accurate SubQ
809 - plays tracks without retry play
811 Wild 9: skip PREGAP + starting accurate SubQ
812 - plays tracks without retry play
814 Find_CurTrack(cdr.SetSectorPlay);
815 generate_subq(cdr.SetSectorPlay);
816 cdr.LocL[0] = LOCL_INVALID;
817 cdr.SubqForwardSectors = 1;
818 cdr.TrackChanged = FALSE;
822 CDR_play(cdr.SetSectorPlay);
824 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
826 // BIOS player - set flag again
829 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
834 // TODO: error 80 if stopped
837 // GameShark CD Player: Calls 2x + Play 2x
839 cdr.FastBackward = 0;
845 // GameShark CD Player: Calls 2x + Play 2x
846 cdr.FastBackward = 1;
851 if (cdr.DriveState != DRIVESTATE_STOPPED) {
852 error = ERROR_INVALIDARG;
855 second_resp_time = cdReadTime * 125 / 2;
859 case CdlStandby + CMD_PART2:
865 // grab time for current track
866 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
868 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
869 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
870 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
875 SetPlaySeekRead(cdr.StatP, 0);
876 cdr.StatP &= ~STATUS_ROTATING;
877 cdr.LocL[0] = LOCL_INVALID;
879 second_resp_time = 0x800;
880 if (cdr.DriveState == DRIVESTATE_STANDBY)
881 second_resp_time = cdReadTime * 30 / 2;
883 cdr.DriveState = DRIVESTATE_STOPPED;
886 case CdlStop + CMD_PART2:
894 Gundam Battle Assault 2: much slower (*)
895 - Fixes boot, gameplay
897 Hokuto no Ken 2: slower
898 - Fixes intro + subtitles
900 InuYasha - Feudal Fairy Tale: slower
903 /* Gameblabla - Tightening the timings (as taken from Duckstation).
904 * The timings from Duckstation are based upon hardware tests.
905 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
906 * seems to be timing sensitive as it can depend on the CPU's clock speed.
908 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
910 second_resp_time = 7000;
914 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
916 SetPlaySeekRead(cdr.StatP, 0);
919 case CdlPause + CMD_PART2:
924 case CdlReset + CMD_WHILE_NOT_READY:
927 SetPlaySeekRead(cdr.StatP, 0);
928 cdr.LocL[0] = LOCL_INVALID;
930 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
931 second_resp_time = not_ready ? 70000 : 4100000;
935 case CdlReset + CMD_PART2:
936 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
949 cdr.File = cdr.Param[0];
950 cdr.Channel = cdr.Param[1];
954 case CdlSetmode + CMD_WHILE_NOT_READY:
955 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
956 cdr.Mode = cdr.Param[0];
960 case CdlGetparam + CMD_WHILE_NOT_READY:
961 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
963 cdr.Result[1] = cdr.Mode;
965 cdr.Result[3] = cdr.File;
966 cdr.Result[4] = cdr.Channel;
970 if (cdr.LocL[0] == LOCL_INVALID) {
975 memcpy(cdr.Result, cdr.LocL, 8);
980 memcpy(&cdr.Result, &cdr.subq, 8);
983 case CdlReadT: // SetSession?
985 second_resp_time = cdReadTime * 290 / 4;
989 case CdlReadT + CMD_PART2:
995 if (CDR_getTN(cdr.ResultTN) == -1) {
996 cdr.Stat = DiskError;
997 cdr.Result[0] |= STATUS_ERROR;
999 cdr.Stat = Acknowledge;
1000 cdr.Result[1] = itob(cdr.ResultTN[0]);
1001 cdr.Result[2] = itob(cdr.ResultTN[1]);
1006 cdr.Track = btoi(cdr.Param[0]);
1008 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1009 cdr.Stat = DiskError;
1010 cdr.Result[0] |= STATUS_ERROR;
1012 cdr.Stat = Acknowledge;
1013 cdr.Result[0] = cdr.StatP;
1014 cdr.Result[1] = itob(cdr.ResultTD[2]);
1015 cdr.Result[2] = itob(cdr.ResultTD[1]);
1016 /* According to Nocash's documentation, the function doesn't care about ff.
1017 * This can be seen also in Mednafen's implementation. */
1018 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1026 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1028 seekTime = cdrSeekTime(cdr.SetSector);
1029 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1031 Crusaders of Might and Magic = 0.5x-4x
1032 - fix cutscene speech start
1034 Eggs of Steel = 2x-?
1038 - fix cutscene speech
1043 second_resp_time = cdReadTime + seekTime;
1047 case CdlSeekL + CMD_PART2:
1048 case CdlSeekP + CMD_PART2:
1049 SetPlaySeekRead(cdr.StatP, 0);
1050 cdr.Result[0] = cdr.StatP;
1051 cdr.Stat = Complete;
1053 Find_CurTrack(cdr.SetSectorPlay);
1054 read_ok = ReadTrack(cdr.SetSectorPlay);
1055 if (read_ok && (buf = CDR_getBuffer()))
1056 memcpy(cdr.LocL, buf, 8);
1057 UpdateSubq(cdr.SetSectorPlay);
1058 cdr.TrackChanged = FALSE;
1062 case CdlTest + CMD_WHILE_NOT_READY:
1063 switch (cdr.Param[0]) {
1064 case 0x20: // System Controller ROM Version
1066 memcpy(cdr.Result, Test20, 4);
1070 memcpy(cdr.Result, Test22, 4);
1072 case 0x23: case 0x24:
1074 memcpy(cdr.Result, Test23, 4);
1080 second_resp_time = 20480;
1083 case CdlID + CMD_PART2:
1085 cdr.Result[0] = cdr.StatP;
1090 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1091 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1092 cdr.Result[1] = 0xc0;
1096 cdr.Result[1] |= 0x10;
1097 if (CdromId[0] == '\0')
1098 cdr.Result[1] |= 0x80;
1100 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1102 /* This adds the string "PCSX" in Playstation bios boot screen */
1103 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1104 cdr.Stat = Complete;
1108 case CdlInit + CMD_WHILE_NOT_READY:
1111 SetPlaySeekRead(cdr.StatP, 0);
1112 // yes, it really sets STATUS_SHELLOPEN
1113 cdr.StatP |= STATUS_SHELLOPEN;
1114 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1120 case CdlGetQ + CMD_WHILE_NOT_READY:
1124 case CdlReadToc + CMD_WHILE_NOT_READY:
1125 cdr.LocL[0] = LOCL_INVALID;
1126 second_resp_time = cdReadTime * 180 / 4;
1130 case CdlReadToc + CMD_PART2:
1131 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1132 cdr.Stat = Complete;
1137 if (cdr.Reading && !cdr.SetlocPending)
1140 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1142 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1143 // Read* acts as play for cdda tracks in cdda mode
1147 if (cdr.SetlocPending) {
1148 seekTime = cdrSeekTime(cdr.SetSector);
1149 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1150 cdr.SetlocPending = 0;
1153 cdr.FirstSector = 1;
1155 // Fighting Force 2 - update subq time immediately
1157 UpdateSubq(cdr.SetSectorPlay);
1158 cdr.LocL[0] = LOCL_INVALID;
1159 cdr.SubqForwardSectors = 1;
1161 cycles = (cdr.Mode & 0x80) ? cdReadTime : cdReadTime * 2;
1163 if (Config.hacks.cdr_read_timing)
1164 cycles = cdrAlignTimingHack(cycles);
1165 CDRPLAYREAD_INT(cycles, 1);
1167 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1173 error = ERROR_INVALIDCMD;
1177 CDR_LOG_I("cdrom: cmd %02x error %02x\n", Cmd, error);
1179 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1180 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1181 cdr.Stat = DiskError;
1185 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1186 cdr.DriveState = DRIVESTATE_STANDBY;
1187 cdr.StatP |= STATUS_ROTATING;
1190 if (second_resp_time) {
1191 cdr.CmdInProgress = Cmd | 0x100;
1192 CDR_INT(second_resp_time);
1194 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1195 cdr.CmdInProgress = cdr.Cmd;
1196 CDR_LOG_I("%u cdrom: cmd %02x came before %02x finished\n",
1197 psxRegs.cycle, cdr.Cmd, Cmd);
1204 #define ssat32_to_16(v) \
1205 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1207 #define ssat32_to_16(v) do { \
1208 if (v < -32768) v = -32768; \
1209 else if (v > 32767) v = 32767; \
1213 static void cdrPrepCdda(s16 *buf, int samples)
1215 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1217 for (i = 0; i < samples; i++) {
1218 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1219 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1224 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1227 int ll = cdr.AttenuatorLeftToLeft;
1228 int lr = cdr.AttenuatorLeftToRight;
1229 int rl = cdr.AttenuatorRightToLeft;
1230 int rr = cdr.AttenuatorRightToRight;
1232 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1235 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1239 for (i = 0; i < samples; i++) {
1242 l = (l * ll + r * rl) >> 7;
1243 r = (r * rr + l * lr) >> 7;
1251 for (i = 0; i < samples; i++) {
1253 l = l * (ll + rl) >> 7;
1254 //r = r * (rr + lr) >> 7;
1262 static void cdrReadInterruptSetResult(unsigned char result)
1265 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1266 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1267 cdr.CmdInProgress, cdr.Stat);
1268 cdr.Irq1Pending = result;
1272 cdr.Result[0] = result;
1273 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1277 static void cdrUpdateTransferBuf(const u8 *buf)
1281 memcpy(cdr.Transfer, buf, DATA_SIZE);
1282 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1283 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1284 if (cdr.FifoOffset < 2048 + 12)
1285 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1288 static void cdrReadInterrupt(void)
1290 u8 *buf = NULL, *hdr;
1294 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1295 msfiAdd(subqPos, cdr.SubqForwardSectors);
1296 UpdateSubq(subqPos);
1297 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1298 cdr.SubqForwardSectors++;
1299 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1303 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1304 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1306 read_ok = ReadTrack(cdr.SetSectorPlay);
1308 buf = CDR_getBuffer();
1313 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1314 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1317 memcpy(cdr.LocL, buf, 8);
1319 if (!cdr.Irq1Pending)
1320 cdrUpdateTransferBuf(buf);
1322 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1324 // Firemen 2: Multi-XA files - briefings, cutscenes
1325 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1327 cdr.Channel = hdr[1];
1331 * Skips playing on channel 255.
1332 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1333 * TODO : Check if this is the proper behaviour.
1335 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1336 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1338 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1339 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1340 cdr.FirstSector = 0;
1342 else cdr.FirstSector = -1;
1347 Croc 2: $40 - only FORM1 (*)
1348 Judge Dredd: $C8 - only FORM1 (*)
1349 Sim Theme Park - no adpcm at all (zero)
1352 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1353 cdrReadInterruptSetResult(cdr.StatP);
1355 msfiAdd(cdr.SetSectorPlay, 1);
1357 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1366 bit 5 - 1 result ready
1368 bit 7 - 1 command being processed
1371 unsigned char cdrRead0(void) {
1372 if (cdr.ResultReady)
1377 cdr.Ctrl |= 0x40; // data fifo not empty
1379 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1382 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1384 return psxHu8(0x1800) = cdr.Ctrl;
1387 void cdrWrite0(unsigned char rt) {
1388 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1390 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1393 unsigned char cdrRead1(void) {
1394 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1395 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1399 if (cdr.ResultP == cdr.ResultC)
1400 cdr.ResultReady = 0;
1402 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1404 return psxHu8(0x1801);
1407 void cdrWrite1(unsigned char rt) {
1408 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1409 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1411 switch (cdr.Ctrl & 3) {
1415 cdr.AttenuatorRightToRightT = rt;
1421 #ifdef CDR_LOG_CMD_IRQ
1422 SysPrintf("%u cdrom: CD1 write: %x (%s)", psxRegs.cycle, rt, CmdName[rt]);
1425 SysPrintf(" Param[%d] = {", cdr.ParamC);
1426 for (i = 0; i < cdr.ParamC; i++)
1427 SysPrintf(" %x,", cdr.Param[i]);
1434 cdr.ResultReady = 0;
1437 if (!cdr.CmdInProgress) {
1438 cdr.CmdInProgress = rt;
1439 // should be something like 12k + controller delays
1443 CDR_LOG_I("%u cdrom: cmd while busy: %02x, prev %02x, busy %02x\n",
1444 psxRegs.cycle, rt, cdr.Cmd, cdr.CmdInProgress);
1445 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1446 cdr.CmdInProgress = rt;
1452 unsigned char cdrRead2(void) {
1453 unsigned char ret = cdr.Transfer[0x920];
1455 if (cdr.FifoOffset < cdr.FifoSize)
1456 ret = cdr.Transfer[cdr.FifoOffset++];
1458 CDR_LOG_I("cdrom: read empty fifo (%d)\n", cdr.FifoSize);
1460 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1464 void cdrWrite2(unsigned char rt) {
1465 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1466 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1468 switch (cdr.Ctrl & 3) {
1470 if (cdr.ParamC < 8) // FIXME: size and wrapping
1471 cdr.Param[cdr.ParamC++] = rt;
1478 cdr.AttenuatorLeftToLeftT = rt;
1481 cdr.AttenuatorRightToLeftT = rt;
1486 unsigned char cdrRead3(void) {
1488 psxHu8(0x1803) = cdr.Stat | 0xE0;
1490 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1492 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1493 return psxHu8(0x1803);
1496 void cdrWrite3(unsigned char rt) {
1497 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1498 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1500 switch (cdr.Ctrl & 3) {
1504 if (cdr.Stat & rt) {
1505 #ifdef CDR_LOG_CMD_IRQ
1506 SysPrintf("%u cdrom: ack %02x (w %02x)\n",
1507 psxRegs.cycle, cdr.Stat & rt, rt);
1509 // note: Croc vs Discworld Noir
1510 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) &&
1511 (cdr.CmdInProgress || cdr.Irq1Pending))
1512 CDR_INT(850); // 711-993
1520 cdr.AttenuatorLeftToRightT = rt;
1524 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1525 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1526 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1527 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1533 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1534 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1536 else if (rt & 0x80) {
1537 switch (cdr.Mode & 0x30) {
1538 case MODE_SIZE_2328:
1540 cdr.FifoOffset = 12;
1541 cdr.FifoSize = 2048 + 12;
1544 case MODE_SIZE_2340:
1547 cdr.FifoSize = 2340;
1551 else if (!(rt & 0xc0))
1552 cdr.FifoOffset = DATA_SIZE; // fifo empty
1555 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1560 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1562 switch (chcr & 0x71000000) {
1564 ptr = (u8 *)PSXM(madr);
1565 if (ptr == INVALID_PTR) {
1566 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1570 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1573 GS CDX: Enhancement CD crash
1576 - Spams DMA3 and gets buffer overrun
1578 size = DATA_SIZE - cdr.FifoOffset;
1583 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1584 cdr.FifoOffset += size;
1586 if (size < cdsize) {
1587 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1588 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1590 psxCpu->Clear(madr, cdsize / 4);
1592 CDRDMA_INT((cdsize/4) * 24);
1594 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1596 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1597 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1601 psxRegs.cycle += (cdsize/4) * 24 - 20;
1606 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1610 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1614 void cdrDmaInterrupt(void)
1616 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1618 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1623 static void getCdInfo(void)
1627 CDR_getTN(cdr.ResultTN);
1628 CDR_getTD(0, cdr.SetSectorEnd);
1629 tmp = cdr.SetSectorEnd[0];
1630 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1631 cdr.SetSectorEnd[2] = tmp;
1635 memset(&cdr, 0, sizeof(cdr));
1641 cdr.FifoOffset = DATA_SIZE; // fifo empty
1642 if (CdromId[0] == '\0') {
1643 cdr.DriveState = DRIVESTATE_STOPPED;
1647 cdr.DriveState = DRIVESTATE_STANDBY;
1648 cdr.StatP = STATUS_ROTATING;
1651 // BIOS player - default values
1652 cdr.AttenuatorLeftToLeft = 0x80;
1653 cdr.AttenuatorLeftToRight = 0x00;
1654 cdr.AttenuatorRightToLeft = 0x00;
1655 cdr.AttenuatorRightToRight = 0x80;
1660 int cdrFreeze(void *f, int Mode) {
1664 if (Mode == 0 && !Config.Cdda)
1667 cdr.freeze_ver = 0x63647202;
1668 gzfreeze(&cdr, sizeof(cdr));
1671 cdr.ParamP = cdr.ParamC;
1672 tmp = cdr.FifoOffset;
1675 gzfreeze(&tmp, sizeof(tmp));
1680 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1681 cdr.FifoSize = (cdr.Mode & 0x20) ? 2340 : 2048 + 12;
1682 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1683 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1685 // read right sub data
1686 tmpp[0] = btoi(cdr.Prev[0]);
1687 tmpp[1] = btoi(cdr.Prev[1]);
1688 tmpp[2] = btoi(cdr.Prev[2]);
1693 if (cdr.freeze_ver < 0x63647202)
1694 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1696 Find_CurTrack(cdr.SetSectorPlay);
1698 CDR_play(cdr.SetSectorPlay);
1699 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1700 CDRPLAYREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1703 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1704 // old versions did not latch Reg2, have to fixup..
1705 if (cdr.Reg2 == 0) {
1706 SysPrintf("cdrom: fixing up old savestate\n");
1709 // also did not save Attenuator..
1710 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1711 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1713 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1721 void LidInterrupt(void) {
1723 cdrLidSeekInterrupt();