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 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
329 cdr.DriveState = DRIVESTATE_LID_OPEN;
334 case DRIVESTATE_LID_OPEN:
335 if (CDR_getStatus(&stat) == -1)
336 stat.Status &= ~STATUS_SHELLOPEN;
339 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
340 cdr.StatP |= STATUS_SHELLOPEN;
342 // could generate error irq here, but real hardware
343 // only sometimes does that
344 // (not done when lots of commands are sent?)
346 CDRLID_INT(cdReadTime * 30);
349 else if (cdr.StatP & STATUS_ROTATING) {
350 cdr.StatP &= ~STATUS_ROTATING;
352 else if (!(stat.Status & STATUS_SHELLOPEN)) {
356 // cdr.StatP STATUS_SHELLOPEN is "sticky"
357 // and is only cleared by CdlNop
359 cdr.DriveState = DRIVESTATE_RESCAN_CD;
360 CDRLID_INT(cdReadTime * 105);
365 CDRLID_INT(cdReadTime * 3);
368 case DRIVESTATE_RESCAN_CD:
369 cdr.StatP |= STATUS_ROTATING;
370 cdr.DriveState = DRIVESTATE_PREPARE_CD;
372 // this is very long on real hardware, over 6 seconds
373 // make it a bit faster here...
374 CDRLID_INT(cdReadTime * 150);
377 case DRIVESTATE_PREPARE_CD:
378 if (cdr.StatP & STATUS_SEEK) {
379 SetPlaySeekRead(cdr.StatP, 0);
380 cdr.DriveState = DRIVESTATE_STANDBY;
383 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
384 CDRLID_INT(cdReadTime * 26);
390 static void Find_CurTrack(const u8 *time)
394 current = msf2sec(time);
396 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
397 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
398 sect = fsm2sec(cdr.ResultTD);
399 if (sect - current >= 150)
404 static void generate_subq(const u8 *time)
406 unsigned char start[3], next[3];
407 unsigned int this_s, start_s, next_s, pregap;
410 CDR_getTD(cdr.CurTrack, start);
411 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
413 CDR_getTD(cdr.CurTrack + 1, next);
416 // last track - cd size
418 next[0] = cdr.SetSectorEnd[2];
419 next[1] = cdr.SetSectorEnd[1];
420 next[2] = cdr.SetSectorEnd[0];
423 this_s = msf2sec(time);
424 start_s = fsm2sec(start);
425 next_s = fsm2sec(next);
427 cdr.TrackChanged = FALSE;
429 if (next_s - this_s < pregap) {
430 cdr.TrackChanged = TRUE;
437 relative_s = this_s - start_s;
438 if (relative_s < 0) {
440 relative_s = -relative_s;
442 sec2msf(relative_s, cdr.subq.Relative);
444 cdr.subq.Track = itob(cdr.CurTrack);
445 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
446 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
447 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
448 cdr.subq.Absolute[0] = itob(time[0]);
449 cdr.subq.Absolute[1] = itob(time[1]);
450 cdr.subq.Absolute[2] = itob(time[2]);
453 static int ReadTrack(const u8 *time)
455 unsigned char tmp[3];
458 tmp[0] = itob(time[0]);
459 tmp[1] = itob(time[1]);
460 tmp[2] = itob(time[2]);
462 if (memcmp(cdr.Prev, tmp, 3) == 0)
465 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
467 read_ok = CDR_readTrack(tmp);
469 memcpy(cdr.Prev, tmp, 3);
473 static void UpdateSubq(const u8 *time)
475 const struct SubQ *subq;
481 subq = (struct SubQ *)CDR_getBufferSub(MSF2SECT(time[0], time[1], time[2]));
482 if (subq != NULL && cdr.CurTrack == 1) {
483 crc = calcCrc((u8 *)subq + 12, 10);
484 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
485 cdr.subq.Track = subq->TrackNumber;
486 cdr.subq.Index = subq->IndexNumber;
487 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
488 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
491 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
492 time[0], time[1], time[2]);
499 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
500 cdr.subq.Track, cdr.subq.Index,
501 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
502 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
505 static void cdrPlayInterrupt_Autopause()
508 boolean abs_lev_chselect;
511 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
512 CDR_LOG( "CDDA STOP\n" );
514 // Magic the Gathering
515 // - looping territory cdda
518 //cdr.ResultReady = 1;
519 //cdr.Stat = DataReady;
521 setIrq(0x1000); // 0x1000 just for logging purposes
524 SetPlaySeekRead(cdr.StatP, 0);
526 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
527 cdr.Result[0] = cdr.StatP;
528 cdr.Result[1] = cdr.subq.Track;
529 cdr.Result[2] = cdr.subq.Index;
531 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
533 /* 8 is a hack. For accuracy, it should be 588. */
534 for (i = 0; i < 8; i++)
536 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
538 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
539 abs_lev_max |= abs_lev_chselect << 15;
541 if (cdr.subq.Absolute[2] & 0x10) {
542 cdr.Result[3] = cdr.subq.Relative[0];
543 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
544 cdr.Result[5] = cdr.subq.Relative[2];
547 cdr.Result[3] = cdr.subq.Absolute[0];
548 cdr.Result[4] = cdr.subq.Absolute[1];
549 cdr.Result[5] = cdr.subq.Absolute[2];
552 cdr.Result[6] = abs_lev_max >> 0;
553 cdr.Result[7] = abs_lev_max >> 8;
555 // Rayman: Logo freeze (resultready + dataready)
557 cdr.Stat = DataReady;
564 static int cdrSeekTime(unsigned char *target)
566 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
567 int seekTime = abs(diff) * (cdReadTime / 200);
570 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
571 * and was unreliable for that game.
572 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
574 * Obviously, this isn't perfect but right now, it should be a bit better.
575 * Games to test this against if you change that setting :
576 * - Driver (titlescreen music delay and retry mission)
577 * - Worms Pinball (Will either not boot or crash in the memory card screen)
578 * - Viewpoint (short pauses if the delay in the ingame music is too long)
580 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
581 * However, 1000000 is not enough for Worms Pinball to reliably boot.
583 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
584 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
588 static u32 cdrAlignTimingHack(u32 cycles)
591 * timing hack for T'ai Fu - Wrath of the Tiger:
592 * The game has a bug where it issues some cdc commands from a low priority
593 * vint handler, however there is a higher priority default bios handler
594 * that acks the vint irq and returns, so game's handler is not reached
595 * (see bios irq handler chains at e004 and the game's irq handling func
596 * at 80036810). For the game to work, vint has to arrive after the bios
597 * vint handler rejects some other irq (of which only cd and rcnt2 are
598 * active), but before the game's handler loop reads I_STAT. The time
599 * window for this is quite small (~1k cycles of so). Apparently this
600 * somehow happens naturally on the real hardware.
602 * Note: always enforcing this breaks other games like Crash PAL version
603 * (inputs get dropped because bios handler doesn't see interrupts).
606 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
608 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
609 vint_rel += PSXCLK / 60;
610 while ((s32)(vint_rel - cycles) < 0)
611 vint_rel += PSXCLK / 60;
615 static void cdrUpdateTransferBuf(const u8 *buf);
616 static void cdrReadInterrupt(void);
617 static void cdrPrepCdda(s16 *buf, int samples);
618 static void cdrAttenuate(s16 *buf, int samples, int stereo);
620 static void msfiAdd(u8 *msfi, u32 count)
634 void cdrPlayReadInterrupt(void)
641 if (!cdr.Play) return;
643 CDR_LOG( "CDDA - %d:%d:%d\n",
644 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
646 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
647 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
649 SetPlaySeekRead(cdr.StatP, 0);
650 cdr.TrackChanged = TRUE;
653 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
656 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
657 cdrPlayInterrupt_Autopause();
659 if (!cdr.Muted && !Config.Cdda) {
660 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
661 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
662 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
666 msfiAdd(cdr.SetSectorPlay, 1);
668 // update for CdlGetlocP/autopause
669 generate_subq(cdr.SetSectorPlay);
671 CDRPLAYREAD_INT(cdReadTime, 0);
674 #define CMD_PART2 0x100
675 #define CMD_WHILE_NOT_READY 0x200
677 void cdrInterrupt(void) {
678 int start_rotating = 0;
680 u32 cycles, seekTime = 0;
681 u32 second_resp_time = 0;
691 CDR_LOG_I("%u cdrom: cmd %02x with irqstat %x\n",
692 psxRegs.cycle, cdr.CmdInProgress, cdr.Stat);
695 if (cdr.Irq1Pending) {
696 // hand out the "newest" sector, according to nocash
697 cdrUpdateTransferBuf(CDR_getBuffer());
698 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
699 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
701 cdr.Result[0] = cdr.Irq1Pending;
702 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
710 cdr.Result[0] = cdr.StatP;
711 cdr.Stat = Acknowledge;
713 Cmd = cdr.CmdInProgress;
714 cdr.CmdInProgress = 0;
723 switch (cdr.DriveState) {
724 case DRIVESTATE_PREPARE_CD:
726 // Syphon filter 2 expects commands to work shortly after it sees
727 // STATUS_ROTATING, so give up trying to emulate the startup seq
728 cdr.DriveState = DRIVESTATE_STANDBY;
729 cdr.StatP &= ~STATUS_SEEK;
730 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
734 case DRIVESTATE_LID_OPEN:
735 case DRIVESTATE_RESCAN_CD:
736 // no disk or busy with the initial scan, allowed cmds are limited
737 not_ready = CMD_WHILE_NOT_READY;
741 switch (Cmd | not_ready) {
743 case CdlNop + CMD_WHILE_NOT_READY:
744 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
745 cdr.StatP &= ~STATUS_SHELLOPEN;
749 case CdlSetloc + CMD_WHILE_NOT_READY:
750 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
752 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
753 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))
755 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
756 error = ERROR_INVALIDARG;
761 for (i = 0; i < 3; i++)
762 set_loc[i] = btoi(cdr.Param[i]);
763 memcpy(cdr.SetSector, set_loc, 3);
764 cdr.SetSector[3] = 0;
765 cdr.SetlocPending = 1;
774 cdr.FastBackward = 0;
778 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
780 if (ParamC != 0 && cdr.Param[0] != 0) {
781 int track = btoi( cdr.Param[0] );
783 if (track <= cdr.ResultTN[1])
784 cdr.CurTrack = track;
786 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
788 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
789 for (i = 0; i < 3; i++)
790 set_loc[i] = cdr.ResultTD[2 - i];
791 seekTime = cdrSeekTime(set_loc);
792 memcpy(cdr.SetSectorPlay, set_loc, 3);
795 else if (cdr.SetlocPending) {
796 seekTime = cdrSeekTime(cdr.SetSector);
797 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
800 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
801 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
803 cdr.SetlocPending = 0;
806 Rayman: detect track changes
809 Twisted Metal 2: skip PREGAP + starting accurate SubQ
810 - plays tracks without retry play
812 Wild 9: skip PREGAP + starting accurate SubQ
813 - plays tracks without retry play
815 Find_CurTrack(cdr.SetSectorPlay);
816 generate_subq(cdr.SetSectorPlay);
817 cdr.LocL[0] = LOCL_INVALID;
818 cdr.SubqForwardSectors = 1;
819 cdr.TrackChanged = FALSE;
823 CDR_play(cdr.SetSectorPlay);
825 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
827 // BIOS player - set flag again
830 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
835 // TODO: error 80 if stopped
838 // GameShark CD Player: Calls 2x + Play 2x
840 cdr.FastBackward = 0;
846 // GameShark CD Player: Calls 2x + Play 2x
847 cdr.FastBackward = 1;
852 if (cdr.DriveState != DRIVESTATE_STOPPED) {
853 error = ERROR_INVALIDARG;
856 second_resp_time = cdReadTime * 125 / 2;
860 case CdlStandby + CMD_PART2:
866 // grab time for current track
867 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
869 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
870 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
871 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
876 SetPlaySeekRead(cdr.StatP, 0);
877 cdr.StatP &= ~STATUS_ROTATING;
878 cdr.LocL[0] = LOCL_INVALID;
880 second_resp_time = 0x800;
881 if (cdr.DriveState == DRIVESTATE_STANDBY)
882 second_resp_time = cdReadTime * 30 / 2;
884 cdr.DriveState = DRIVESTATE_STOPPED;
887 case CdlStop + CMD_PART2:
895 Gundam Battle Assault 2: much slower (*)
896 - Fixes boot, gameplay
898 Hokuto no Ken 2: slower
899 - Fixes intro + subtitles
901 InuYasha - Feudal Fairy Tale: slower
904 /* Gameblabla - Tightening the timings (as taken from Duckstation).
905 * The timings from Duckstation are based upon hardware tests.
906 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
907 * seems to be timing sensitive as it can depend on the CPU's clock speed.
909 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
911 second_resp_time = 7000;
915 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
917 SetPlaySeekRead(cdr.StatP, 0);
920 case CdlPause + CMD_PART2:
925 case CdlReset + CMD_WHILE_NOT_READY:
928 SetPlaySeekRead(cdr.StatP, 0);
929 cdr.LocL[0] = LOCL_INVALID;
931 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
932 second_resp_time = not_ready ? 70000 : 4100000;
936 case CdlReset + CMD_PART2:
937 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
950 cdr.File = cdr.Param[0];
951 cdr.Channel = cdr.Param[1];
955 case CdlSetmode + CMD_WHILE_NOT_READY:
956 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
957 cdr.Mode = cdr.Param[0];
961 case CdlGetparam + CMD_WHILE_NOT_READY:
962 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
964 cdr.Result[1] = cdr.Mode;
966 cdr.Result[3] = cdr.File;
967 cdr.Result[4] = cdr.Channel;
971 if (cdr.LocL[0] == LOCL_INVALID) {
976 memcpy(cdr.Result, cdr.LocL, 8);
981 memcpy(&cdr.Result, &cdr.subq, 8);
984 case CdlReadT: // SetSession?
986 second_resp_time = cdReadTime * 290 / 4;
990 case CdlReadT + CMD_PART2:
996 if (CDR_getTN(cdr.ResultTN) == -1) {
997 cdr.Stat = DiskError;
998 cdr.Result[0] |= STATUS_ERROR;
1000 cdr.Stat = Acknowledge;
1001 cdr.Result[1] = itob(cdr.ResultTN[0]);
1002 cdr.Result[2] = itob(cdr.ResultTN[1]);
1007 cdr.Track = btoi(cdr.Param[0]);
1009 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1010 cdr.Stat = DiskError;
1011 cdr.Result[0] |= STATUS_ERROR;
1013 cdr.Stat = Acknowledge;
1014 cdr.Result[0] = cdr.StatP;
1015 cdr.Result[1] = itob(cdr.ResultTD[2]);
1016 cdr.Result[2] = itob(cdr.ResultTD[1]);
1017 /* According to Nocash's documentation, the function doesn't care about ff.
1018 * This can be seen also in Mednafen's implementation. */
1019 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1027 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1029 seekTime = cdrSeekTime(cdr.SetSector);
1030 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1032 Crusaders of Might and Magic = 0.5x-4x
1033 - fix cutscene speech start
1035 Eggs of Steel = 2x-?
1039 - fix cutscene speech
1044 second_resp_time = cdReadTime + seekTime;
1048 case CdlSeekL + CMD_PART2:
1049 case CdlSeekP + CMD_PART2:
1050 SetPlaySeekRead(cdr.StatP, 0);
1051 cdr.Result[0] = cdr.StatP;
1052 cdr.Stat = Complete;
1054 Find_CurTrack(cdr.SetSectorPlay);
1055 read_ok = ReadTrack(cdr.SetSectorPlay);
1056 if (read_ok && (buf = CDR_getBuffer()))
1057 memcpy(cdr.LocL, buf, 8);
1058 UpdateSubq(cdr.SetSectorPlay);
1059 cdr.TrackChanged = FALSE;
1063 case CdlTest + CMD_WHILE_NOT_READY:
1064 switch (cdr.Param[0]) {
1065 case 0x20: // System Controller ROM Version
1067 memcpy(cdr.Result, Test20, 4);
1071 memcpy(cdr.Result, Test22, 4);
1073 case 0x23: case 0x24:
1075 memcpy(cdr.Result, Test23, 4);
1081 second_resp_time = 20480;
1084 case CdlID + CMD_PART2:
1086 cdr.Result[0] = cdr.StatP;
1091 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1092 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1093 cdr.Result[1] = 0xc0;
1097 cdr.Result[1] |= 0x10;
1098 if (CdromId[0] == '\0')
1099 cdr.Result[1] |= 0x80;
1101 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1103 /* This adds the string "PCSX" in Playstation bios boot screen */
1104 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1105 cdr.Stat = Complete;
1109 case CdlInit + CMD_WHILE_NOT_READY:
1112 SetPlaySeekRead(cdr.StatP, 0);
1113 // yes, it really sets STATUS_SHELLOPEN
1114 cdr.StatP |= STATUS_SHELLOPEN;
1115 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1121 case CdlGetQ + CMD_WHILE_NOT_READY:
1125 case CdlReadToc + CMD_WHILE_NOT_READY:
1126 cdr.LocL[0] = LOCL_INVALID;
1127 second_resp_time = cdReadTime * 180 / 4;
1131 case CdlReadToc + CMD_PART2:
1132 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1133 cdr.Stat = Complete;
1138 if (cdr.Reading && !cdr.SetlocPending)
1141 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1143 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1144 // Read* acts as play for cdda tracks in cdda mode
1148 if (cdr.SetlocPending) {
1149 seekTime = cdrSeekTime(cdr.SetSector);
1150 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1151 cdr.SetlocPending = 0;
1154 cdr.FirstSector = 1;
1156 // Fighting Force 2 - update subq time immediately
1158 UpdateSubq(cdr.SetSectorPlay);
1159 cdr.LocL[0] = LOCL_INVALID;
1160 cdr.SubqForwardSectors = 1;
1162 cycles = (cdr.Mode & 0x80) ? cdReadTime : cdReadTime * 2;
1164 if (Config.hacks.cdr_read_timing)
1165 cycles = cdrAlignTimingHack(cycles);
1166 CDRPLAYREAD_INT(cycles, 1);
1168 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1174 error = ERROR_INVALIDCMD;
1178 CDR_LOG_I("cdrom: cmd %02x error %02x\n", Cmd, error);
1180 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1181 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1182 cdr.Stat = DiskError;
1186 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1187 cdr.DriveState = DRIVESTATE_STANDBY;
1188 cdr.StatP |= STATUS_ROTATING;
1191 if (second_resp_time) {
1192 cdr.CmdInProgress = Cmd | 0x100;
1193 CDR_INT(second_resp_time);
1195 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1196 cdr.CmdInProgress = cdr.Cmd;
1197 CDR_LOG_I("%u cdrom: cmd %02x came before %02x finished\n",
1198 psxRegs.cycle, cdr.Cmd, Cmd);
1205 #define ssat32_to_16(v) \
1206 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1208 #define ssat32_to_16(v) do { \
1209 if (v < -32768) v = -32768; \
1210 else if (v > 32767) v = 32767; \
1214 static void cdrPrepCdda(s16 *buf, int samples)
1216 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1218 for (i = 0; i < samples; i++) {
1219 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1220 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1225 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1228 int ll = cdr.AttenuatorLeftToLeft;
1229 int lr = cdr.AttenuatorLeftToRight;
1230 int rl = cdr.AttenuatorRightToLeft;
1231 int rr = cdr.AttenuatorRightToRight;
1233 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1236 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1240 for (i = 0; i < samples; i++) {
1243 l = (l * ll + r * rl) >> 7;
1244 r = (r * rr + l * lr) >> 7;
1252 for (i = 0; i < samples; i++) {
1254 l = l * (ll + rl) >> 7;
1255 //r = r * (rr + lr) >> 7;
1263 static void cdrReadInterruptSetResult(unsigned char result)
1266 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1267 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1268 cdr.CmdInProgress, cdr.Stat);
1269 cdr.Irq1Pending = result;
1273 cdr.Result[0] = result;
1274 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1278 static void cdrUpdateTransferBuf(const u8 *buf)
1282 memcpy(cdr.Transfer, buf, DATA_SIZE);
1283 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1284 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1285 if (cdr.FifoOffset < 2048 + 12)
1286 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1289 static void cdrReadInterrupt(void)
1291 u8 *buf = NULL, *hdr;
1295 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1296 msfiAdd(subqPos, cdr.SubqForwardSectors);
1297 UpdateSubq(subqPos);
1298 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1299 cdr.SubqForwardSectors++;
1300 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1304 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1305 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1307 read_ok = ReadTrack(cdr.SetSectorPlay);
1309 buf = CDR_getBuffer();
1314 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1315 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1318 memcpy(cdr.LocL, buf, 8);
1320 if (!cdr.Irq1Pending)
1321 cdrUpdateTransferBuf(buf);
1323 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1325 // Firemen 2: Multi-XA files - briefings, cutscenes
1326 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1328 cdr.Channel = hdr[1];
1332 * Skips playing on channel 255.
1333 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1334 * TODO : Check if this is the proper behaviour.
1336 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1337 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1339 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1340 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1341 cdr.FirstSector = 0;
1343 else cdr.FirstSector = -1;
1348 Croc 2: $40 - only FORM1 (*)
1349 Judge Dredd: $C8 - only FORM1 (*)
1350 Sim Theme Park - no adpcm at all (zero)
1353 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1354 cdrReadInterruptSetResult(cdr.StatP);
1356 msfiAdd(cdr.SetSectorPlay, 1);
1358 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1367 bit 5 - 1 result ready
1369 bit 7 - 1 command being processed
1372 unsigned char cdrRead0(void) {
1373 if (cdr.ResultReady)
1378 cdr.Ctrl |= 0x40; // data fifo not empty
1380 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1383 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1385 return psxHu8(0x1800) = cdr.Ctrl;
1388 void cdrWrite0(unsigned char rt) {
1389 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1391 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1394 unsigned char cdrRead1(void) {
1395 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1396 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1400 if (cdr.ResultP == cdr.ResultC)
1401 cdr.ResultReady = 0;
1403 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1405 return psxHu8(0x1801);
1408 void cdrWrite1(unsigned char rt) {
1409 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1410 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1412 switch (cdr.Ctrl & 3) {
1416 cdr.AttenuatorRightToRightT = rt;
1422 #ifdef CDR_LOG_CMD_IRQ
1423 SysPrintf("%u cdrom: CD1 write: %x (%s)", psxRegs.cycle, rt, CmdName[rt]);
1426 SysPrintf(" Param[%d] = {", cdr.ParamC);
1427 for (i = 0; i < cdr.ParamC; i++)
1428 SysPrintf(" %x,", cdr.Param[i]);
1435 cdr.ResultReady = 0;
1438 if (!cdr.CmdInProgress) {
1439 cdr.CmdInProgress = rt;
1440 // should be something like 12k + controller delays
1444 CDR_LOG_I("%u cdrom: cmd while busy: %02x, prev %02x, busy %02x\n",
1445 psxRegs.cycle, rt, cdr.Cmd, cdr.CmdInProgress);
1446 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1447 cdr.CmdInProgress = rt;
1453 unsigned char cdrRead2(void) {
1454 unsigned char ret = cdr.Transfer[0x920];
1456 if (cdr.FifoOffset < cdr.FifoSize)
1457 ret = cdr.Transfer[cdr.FifoOffset++];
1459 CDR_LOG_I("cdrom: read empty fifo (%d)\n", cdr.FifoSize);
1461 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1465 void cdrWrite2(unsigned char rt) {
1466 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1467 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1469 switch (cdr.Ctrl & 3) {
1471 if (cdr.ParamC < 8) // FIXME: size and wrapping
1472 cdr.Param[cdr.ParamC++] = rt;
1479 cdr.AttenuatorLeftToLeftT = rt;
1482 cdr.AttenuatorRightToLeftT = rt;
1487 unsigned char cdrRead3(void) {
1489 psxHu8(0x1803) = cdr.Stat | 0xE0;
1491 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1493 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1494 return psxHu8(0x1803);
1497 void cdrWrite3(unsigned char rt) {
1498 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1499 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1501 switch (cdr.Ctrl & 3) {
1505 if (cdr.Stat & rt) {
1506 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1507 + psxRegs.intCycle[PSXINT_CDR].cycle;
1508 #ifdef CDR_LOG_CMD_IRQ
1509 SysPrintf("%u cdrom: ack %02x (w=%02x p=%d,%d)\n",
1510 psxRegs.cycle, cdr.Stat & rt, rt,
1511 !!(psxRegs.interrupt & (1 << PSXINT_CDR)),
1512 nextCycle - psxRegs.cycle);
1514 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1515 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) &&
1516 (cdr.CmdInProgress || cdr.Irq1Pending))
1518 s32 c = 2048 - (psxRegs.cycle - nextCycle);
1519 c = MAX_VALUE(c, 512);
1529 cdr.AttenuatorLeftToRightT = rt;
1533 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1534 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1535 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1536 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1542 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1543 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1545 else if (rt & 0x80) {
1546 switch (cdr.Mode & 0x30) {
1547 case MODE_SIZE_2328:
1549 cdr.FifoOffset = 12;
1550 cdr.FifoSize = 2048 + 12;
1553 case MODE_SIZE_2340:
1556 cdr.FifoSize = 2340;
1560 else if (!(rt & 0xc0))
1561 cdr.FifoOffset = DATA_SIZE; // fifo empty
1564 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1569 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1571 switch (chcr & 0x71000000) {
1573 ptr = (u8 *)PSXM(madr);
1574 if (ptr == INVALID_PTR) {
1575 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1579 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1582 GS CDX: Enhancement CD crash
1585 - Spams DMA3 and gets buffer overrun
1587 size = DATA_SIZE - cdr.FifoOffset;
1592 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1593 cdr.FifoOffset += size;
1595 if (size < cdsize) {
1596 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1597 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1599 psxCpu->Clear(madr, cdsize / 4);
1601 CDRDMA_INT((cdsize/4) * 24);
1603 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1605 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1606 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1610 psxRegs.cycle += (cdsize/4) * 24 - 20;
1615 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1619 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1623 void cdrDmaInterrupt(void)
1625 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1627 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1632 static void getCdInfo(void)
1636 CDR_getTN(cdr.ResultTN);
1637 CDR_getTD(0, cdr.SetSectorEnd);
1638 tmp = cdr.SetSectorEnd[0];
1639 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1640 cdr.SetSectorEnd[2] = tmp;
1644 memset(&cdr, 0, sizeof(cdr));
1650 cdr.FifoOffset = DATA_SIZE; // fifo empty
1651 if (CdromId[0] == '\0') {
1652 cdr.DriveState = DRIVESTATE_STOPPED;
1656 cdr.DriveState = DRIVESTATE_STANDBY;
1657 cdr.StatP = STATUS_ROTATING;
1660 // BIOS player - default values
1661 cdr.AttenuatorLeftToLeft = 0x80;
1662 cdr.AttenuatorLeftToRight = 0x00;
1663 cdr.AttenuatorRightToLeft = 0x00;
1664 cdr.AttenuatorRightToRight = 0x80;
1669 int cdrFreeze(void *f, int Mode) {
1673 if (Mode == 0 && !Config.Cdda)
1676 cdr.freeze_ver = 0x63647202;
1677 gzfreeze(&cdr, sizeof(cdr));
1680 cdr.ParamP = cdr.ParamC;
1681 tmp = cdr.FifoOffset;
1684 gzfreeze(&tmp, sizeof(tmp));
1689 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1690 cdr.FifoSize = (cdr.Mode & 0x20) ? 2340 : 2048 + 12;
1691 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1692 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1694 // read right sub data
1695 tmpp[0] = btoi(cdr.Prev[0]);
1696 tmpp[1] = btoi(cdr.Prev[1]);
1697 tmpp[2] = btoi(cdr.Prev[2]);
1702 if (cdr.freeze_ver < 0x63647202)
1703 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1705 Find_CurTrack(cdr.SetSectorPlay);
1707 CDR_play(cdr.SetSectorPlay);
1708 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1709 CDRPLAYREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1712 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1713 // old versions did not latch Reg2, have to fixup..
1714 if (cdr.Reg2 == 0) {
1715 SysPrintf("cdrom: fixing up old savestate\n");
1718 // also did not save Attenuator..
1719 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1720 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1722 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1730 void LidInterrupt(void) {
1732 cdrLidSeekInterrupt();