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(fmt, ...) \
40 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
43 #define CDR_LOG_IO SysPrintf
45 #define CDR_LOG_IO(...)
47 //#define CDR_LOG_CMD_IRQ
50 // unused members maintain savesate compatibility
51 unsigned char unused0;
52 unsigned char unused1;
54 unsigned char unused2;
60 unsigned char Transfer[DATA_SIZE];
64 unsigned char Relative[3];
65 unsigned char Absolute[3];
67 unsigned char TrackChanged;
68 unsigned char unused3[3];
69 unsigned int freeze_ver;
71 unsigned char Prev[4];
72 unsigned char Param[8];
73 unsigned char Result[16];
77 unsigned char ResultC;
78 unsigned char ResultP;
79 unsigned char ResultReady;
81 unsigned char SubqForwardSectors;
82 unsigned char SetlocPending;
85 unsigned char ResultTN[6];
86 unsigned char ResultTD[4];
87 unsigned char SetSectorPlay[4];
88 unsigned char SetSectorEnd[4];
89 unsigned char SetSector[4];
93 int Mode, File, Channel;
94 unsigned char LocL[8];
114 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
115 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
116 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
117 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
119 static s16 read_buf[CD_FRAMESIZE_RAW/2];
121 /* CD-ROM magic numbers */
122 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
127 #define CdlBackward 5
135 #define CdlSetfilter 13
136 #define CdlSetmode 14
137 #define CdlGetparam 15
138 #define CdlGetlocL 16
139 #define CdlGetlocP 17
145 #define CdlSetclock 23
146 #define CdlGetclock 24
152 #define CdlReadToc 30
154 #ifdef CDR_LOG_CMD_IRQ
155 static const char * const CmdName[0x100] = {
156 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
157 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
158 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
159 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
160 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
161 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
162 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
163 "CdlInit", NULL, "CDlReadToc", NULL
167 unsigned char Test04[] = { 0 };
168 unsigned char Test05[] = { 0 };
169 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
170 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
171 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
177 #define Acknowledge 3
182 #define MODE_SPEED (1<<7) // 0x80
183 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
184 #define MODE_SIZE_2340 (1<<5) // 0x20
185 #define MODE_SIZE_2328 (1<<4) // 0x10
186 #define MODE_SIZE_2048 (0<<4) // 0x00
187 #define MODE_SF (1<<3) // 0x08 channel on/off
188 #define MODE_REPORT (1<<2) // 0x04
189 #define MODE_AUTOPAUSE (1<<1) // 0x02
190 #define MODE_CDDA (1<<0) // 0x01
193 #define STATUS_PLAY (1<<7) // 0x80
194 #define STATUS_SEEK (1<<6) // 0x40
195 #define STATUS_READ (1<<5) // 0x20
196 #define STATUS_SHELLOPEN (1<<4) // 0x10
197 #define STATUS_UNKNOWN3 (1<<3) // 0x08
198 #define STATUS_UNKNOWN2 (1<<2) // 0x04
199 #define STATUS_ROTATING (1<<1) // 0x02
200 #define STATUS_ERROR (1<<0) // 0x01
203 #define ERROR_NOTREADY (1<<7) // 0x80
204 #define ERROR_INVALIDCMD (1<<6) // 0x40
205 #define ERROR_INVALIDARG (1<<5) // 0x20
207 // 1x = 75 sectors per second
208 // PSXCLK = 1 sec in the ps
209 // so (PSXCLK / 75) = cdr read time (linuzappz)
210 #define cdReadTime (PSXCLK / 75)
212 #define LOCL_INVALID 0xff
213 #define SUBQ_FORWARD_SECTORS 2u
216 DRIVESTATE_STANDBY = 0, // pause, play, read
218 DRIVESTATE_RESCAN_CD,
219 DRIVESTATE_PREPARE_CD,
223 static struct CdrStat stat;
225 static unsigned int msf2sec(const u8 *msf) {
226 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
229 // for that weird psemu API..
230 static unsigned int fsm2sec(const u8 *msf) {
231 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
234 static void sec2msf(unsigned int s, u8 *msf) {
235 msf[0] = s / 75 / 60;
236 s = s - msf[0] * 75 * 60;
243 #define CDR_INT(eCycle) { \
244 psxRegs.interrupt |= (1 << PSXINT_CDR); \
245 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
246 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
247 new_dyna_set_event(PSXINT_CDR, eCycle); \
250 // cdrPlayReadInterrupt
251 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
253 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
255 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
257 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
258 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
259 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
262 // cdrLidSeekInterrupt
263 #define CDRLID_INT(eCycle) { \
264 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
265 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
266 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
267 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
270 #define StopReading() { \
272 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
275 #define StopCdda() { \
276 if (cdr.Play && !Config.Cdda) CDR_stop(); \
278 cdr.FastForward = 0; \
279 cdr.FastBackward = 0; \
282 #define SetPlaySeekRead(x, f) { \
283 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
287 #define SetResultSize(size) { \
289 cdr.ResultC = size; \
290 cdr.ResultReady = 1; \
293 static void setIrq(int log_cmd)
295 if (cdr.Stat & cdr.Reg2)
296 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
298 #ifdef CDR_LOG_CMD_IRQ
302 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
303 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
304 for (i = 0; i < cdr.ResultC; i++)
305 SysPrintf("%02x ", cdr.Result[i]);
311 // timing used in this function was taken from tests on real hardware
312 // (yes it's slow, but you probably don't want to modify it)
313 void cdrLidSeekInterrupt(void)
315 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
317 switch (cdr.DriveState) {
319 case DRIVESTATE_STANDBY:
322 SetPlaySeekRead(cdr.StatP, 0);
324 if (CDR_getStatus(&stat) == -1)
327 if (stat.Status & STATUS_SHELLOPEN)
329 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
330 cdr.DriveState = DRIVESTATE_LID_OPEN;
335 case DRIVESTATE_LID_OPEN:
336 if (CDR_getStatus(&stat) == -1)
337 stat.Status &= ~STATUS_SHELLOPEN;
340 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
341 cdr.StatP |= STATUS_SHELLOPEN;
343 // could generate error irq here, but real hardware
344 // only sometimes does that
345 // (not done when lots of commands are sent?)
347 CDRLID_INT(cdReadTime * 30);
350 else if (cdr.StatP & STATUS_ROTATING) {
351 cdr.StatP &= ~STATUS_ROTATING;
353 else if (!(stat.Status & STATUS_SHELLOPEN)) {
357 // cdr.StatP STATUS_SHELLOPEN is "sticky"
358 // and is only cleared by CdlNop
360 cdr.DriveState = DRIVESTATE_RESCAN_CD;
361 CDRLID_INT(cdReadTime * 105);
366 CDRLID_INT(cdReadTime * 3);
369 case DRIVESTATE_RESCAN_CD:
370 cdr.StatP |= STATUS_ROTATING;
371 cdr.DriveState = DRIVESTATE_PREPARE_CD;
373 // this is very long on real hardware, over 6 seconds
374 // make it a bit faster here...
375 CDRLID_INT(cdReadTime * 150);
378 case DRIVESTATE_PREPARE_CD:
379 if (cdr.StatP & STATUS_SEEK) {
380 SetPlaySeekRead(cdr.StatP, 0);
381 cdr.DriveState = DRIVESTATE_STANDBY;
384 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
385 CDRLID_INT(cdReadTime * 26);
391 static void Find_CurTrack(const u8 *time)
395 current = msf2sec(time);
397 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
398 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
399 sect = fsm2sec(cdr.ResultTD);
400 if (sect - current >= 150)
405 static void generate_subq(const u8 *time)
407 unsigned char start[3], next[3];
408 unsigned int this_s, start_s, next_s, pregap;
411 CDR_getTD(cdr.CurTrack, start);
412 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
414 CDR_getTD(cdr.CurTrack + 1, next);
417 // last track - cd size
419 next[0] = cdr.SetSectorEnd[2];
420 next[1] = cdr.SetSectorEnd[1];
421 next[2] = cdr.SetSectorEnd[0];
424 this_s = msf2sec(time);
425 start_s = fsm2sec(start);
426 next_s = fsm2sec(next);
428 cdr.TrackChanged = FALSE;
430 if (next_s - this_s < pregap) {
431 cdr.TrackChanged = TRUE;
438 relative_s = this_s - start_s;
439 if (relative_s < 0) {
441 relative_s = -relative_s;
443 sec2msf(relative_s, cdr.subq.Relative);
445 cdr.subq.Track = itob(cdr.CurTrack);
446 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
447 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
448 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
449 cdr.subq.Absolute[0] = itob(time[0]);
450 cdr.subq.Absolute[1] = itob(time[1]);
451 cdr.subq.Absolute[2] = itob(time[2]);
454 static int ReadTrack(const u8 *time)
456 unsigned char tmp[3];
459 tmp[0] = itob(time[0]);
460 tmp[1] = itob(time[1]);
461 tmp[2] = itob(time[2]);
463 if (memcmp(cdr.Prev, tmp, 3) == 0)
466 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
468 read_ok = CDR_readTrack(tmp);
470 memcpy(cdr.Prev, tmp, 3);
474 static void UpdateSubq(const u8 *time)
476 const struct SubQ *subq;
482 subq = (struct SubQ *)CDR_getBufferSub(MSF2SECT(time[0], time[1], time[2]));
483 if (subq != NULL && cdr.CurTrack == 1) {
484 crc = calcCrc((u8 *)subq + 12, 10);
485 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
486 cdr.subq.Track = subq->TrackNumber;
487 cdr.subq.Index = subq->IndexNumber;
488 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
489 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
492 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
493 time[0], time[1], time[2]);
500 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
501 cdr.subq.Track, cdr.subq.Index,
502 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
503 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
506 static void cdrPlayInterrupt_Autopause()
509 boolean abs_lev_chselect;
512 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
513 CDR_LOG( "CDDA STOP\n" );
515 // Magic the Gathering
516 // - looping territory cdda
519 //cdr.ResultReady = 1;
520 //cdr.Stat = DataReady;
522 setIrq(0x1000); // 0x1000 just for logging purposes
525 SetPlaySeekRead(cdr.StatP, 0);
527 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
528 cdr.Result[0] = cdr.StatP;
529 cdr.Result[1] = cdr.subq.Track;
530 cdr.Result[2] = cdr.subq.Index;
532 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
534 /* 8 is a hack. For accuracy, it should be 588. */
535 for (i = 0; i < 8; i++)
537 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
539 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
540 abs_lev_max |= abs_lev_chselect << 15;
542 if (cdr.subq.Absolute[2] & 0x10) {
543 cdr.Result[3] = cdr.subq.Relative[0];
544 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
545 cdr.Result[5] = cdr.subq.Relative[2];
548 cdr.Result[3] = cdr.subq.Absolute[0];
549 cdr.Result[4] = cdr.subq.Absolute[1];
550 cdr.Result[5] = cdr.subq.Absolute[2];
553 cdr.Result[6] = abs_lev_max >> 0;
554 cdr.Result[7] = abs_lev_max >> 8;
556 // Rayman: Logo freeze (resultready + dataready)
558 cdr.Stat = DataReady;
565 static int cdrSeekTime(unsigned char *target)
567 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
568 int seekTime = abs(diff) * (cdReadTime / 200);
571 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
572 * and was unreliable for that game.
573 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
575 * Obviously, this isn't perfect but right now, it should be a bit better.
576 * Games to test this against if you change that setting :
577 * - Driver (titlescreen music delay and retry mission)
578 * - Worms Pinball (Will either not boot or crash in the memory card screen)
579 * - Viewpoint (short pauses if the delay in the ingame music is too long)
581 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
582 * However, 1000000 is not enough for Worms Pinball to reliably boot.
584 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
585 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
589 static u32 cdrAlignTimingHack(u32 cycles)
592 * timing hack for T'ai Fu - Wrath of the Tiger:
593 * The game has a bug where it issues some cdc commands from a low priority
594 * vint handler, however there is a higher priority default bios handler
595 * that acks the vint irq and returns, so game's handler is not reached
596 * (see bios irq handler chains at e004 and the game's irq handling func
597 * at 80036810). For the game to work, vint has to arrive after the bios
598 * vint handler rejects some other irq (of which only cd and rcnt2 are
599 * active), but before the game's handler loop reads I_STAT. The time
600 * window for this is quite small (~1k cycles of so). Apparently this
601 * somehow happens naturally on the real hardware.
603 * Note: always enforcing this breaks other games like Crash PAL version
604 * (inputs get dropped because bios handler doesn't see interrupts).
607 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
609 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
610 vint_rel += PSXCLK / 60;
611 while ((s32)(vint_rel - cycles) < 0)
612 vint_rel += PSXCLK / 60;
616 static void cdrUpdateTransferBuf(const u8 *buf);
617 static void cdrReadInterrupt(void);
618 static void cdrPrepCdda(s16 *buf, int samples);
619 static void cdrAttenuate(s16 *buf, int samples, int stereo);
621 static void msfiAdd(u8 *msfi, u32 count)
635 void cdrPlayReadInterrupt(void)
642 if (!cdr.Play) return;
644 CDR_LOG( "CDDA - %d:%d:%d\n",
645 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
647 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
648 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
650 SetPlaySeekRead(cdr.StatP, 0);
651 cdr.TrackChanged = TRUE;
654 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
657 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
658 cdrPlayInterrupt_Autopause();
660 if (!cdr.Muted && !Config.Cdda) {
661 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
662 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
663 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
667 msfiAdd(cdr.SetSectorPlay, 1);
669 // update for CdlGetlocP/autopause
670 generate_subq(cdr.SetSectorPlay);
672 CDRPLAYREAD_INT(cdReadTime, 0);
675 #define CMD_PART2 0x100
676 #define CMD_WHILE_NOT_READY 0x200
678 void cdrInterrupt(void) {
679 int start_rotating = 0;
681 u32 cycles, seekTime = 0;
682 u32 second_resp_time = 0;
692 CDR_LOG_I("cmd %02x with irqstat %x\n",
693 cdr.CmdInProgress, cdr.Stat);
696 if (cdr.Irq1Pending) {
697 // hand out the "newest" sector, according to nocash
698 cdrUpdateTransferBuf(CDR_getBuffer());
699 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
700 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
701 cdr.CmdInProgress, cdr.Irq1Pending);
703 cdr.Result[0] = cdr.Irq1Pending;
704 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
712 cdr.Result[0] = cdr.StatP;
713 cdr.Stat = Acknowledge;
715 Cmd = cdr.CmdInProgress;
716 cdr.CmdInProgress = 0;
725 switch (cdr.DriveState) {
726 case DRIVESTATE_PREPARE_CD:
728 // Syphon filter 2 expects commands to work shortly after it sees
729 // STATUS_ROTATING, so give up trying to emulate the startup seq
730 cdr.DriveState = DRIVESTATE_STANDBY;
731 cdr.StatP &= ~STATUS_SEEK;
732 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
736 case DRIVESTATE_LID_OPEN:
737 case DRIVESTATE_RESCAN_CD:
738 // no disk or busy with the initial scan, allowed cmds are limited
739 not_ready = CMD_WHILE_NOT_READY;
743 switch (Cmd | not_ready) {
745 case CdlNop + CMD_WHILE_NOT_READY:
746 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
747 cdr.StatP &= ~STATUS_SHELLOPEN;
751 case CdlSetloc + CMD_WHILE_NOT_READY:
752 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
754 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
755 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))
757 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
758 error = ERROR_INVALIDARG;
763 for (i = 0; i < 3; i++)
764 set_loc[i] = btoi(cdr.Param[i]);
765 memcpy(cdr.SetSector, set_loc, 3);
766 cdr.SetSector[3] = 0;
767 cdr.SetlocPending = 1;
776 cdr.FastBackward = 0;
780 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
782 if (ParamC != 0 && cdr.Param[0] != 0) {
783 int track = btoi( cdr.Param[0] );
785 if (track <= cdr.ResultTN[1])
786 cdr.CurTrack = track;
788 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
790 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
791 for (i = 0; i < 3; i++)
792 set_loc[i] = cdr.ResultTD[2 - i];
793 seekTime = cdrSeekTime(set_loc);
794 memcpy(cdr.SetSectorPlay, set_loc, 3);
797 else if (cdr.SetlocPending) {
798 seekTime = cdrSeekTime(cdr.SetSector);
799 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
802 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
803 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
805 cdr.SetlocPending = 0;
808 Rayman: detect track changes
811 Twisted Metal 2: skip PREGAP + starting accurate SubQ
812 - plays tracks without retry play
814 Wild 9: skip PREGAP + starting accurate SubQ
815 - plays tracks without retry play
817 Find_CurTrack(cdr.SetSectorPlay);
818 generate_subq(cdr.SetSectorPlay);
819 cdr.LocL[0] = LOCL_INVALID;
820 cdr.SubqForwardSectors = 1;
821 cdr.TrackChanged = FALSE;
825 CDR_play(cdr.SetSectorPlay);
827 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
829 // BIOS player - set flag again
832 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
837 // TODO: error 80 if stopped
840 // GameShark CD Player: Calls 2x + Play 2x
842 cdr.FastBackward = 0;
848 // GameShark CD Player: Calls 2x + Play 2x
849 cdr.FastBackward = 1;
854 if (cdr.DriveState != DRIVESTATE_STOPPED) {
855 error = ERROR_INVALIDARG;
858 second_resp_time = cdReadTime * 125 / 2;
862 case CdlStandby + CMD_PART2:
868 // grab time for current track
869 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
871 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
872 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
873 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
878 SetPlaySeekRead(cdr.StatP, 0);
879 cdr.StatP &= ~STATUS_ROTATING;
880 cdr.LocL[0] = LOCL_INVALID;
882 second_resp_time = 0x800;
883 if (cdr.DriveState == DRIVESTATE_STANDBY)
884 second_resp_time = cdReadTime * 30 / 2;
886 cdr.DriveState = DRIVESTATE_STOPPED;
889 case CdlStop + CMD_PART2:
897 Gundam Battle Assault 2: much slower (*)
898 - Fixes boot, gameplay
900 Hokuto no Ken 2: slower
901 - Fixes intro + subtitles
903 InuYasha - Feudal Fairy Tale: slower
906 /* Gameblabla - Tightening the timings (as taken from Duckstation).
907 * The timings from Duckstation are based upon hardware tests.
908 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
909 * seems to be timing sensitive as it can depend on the CPU's clock speed.
911 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
913 second_resp_time = 7000;
917 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
919 SetPlaySeekRead(cdr.StatP, 0);
922 case CdlPause + CMD_PART2:
927 case CdlReset + CMD_WHILE_NOT_READY:
930 SetPlaySeekRead(cdr.StatP, 0);
931 cdr.LocL[0] = LOCL_INVALID;
933 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
934 second_resp_time = not_ready ? 70000 : 4100000;
938 case CdlReset + CMD_PART2:
939 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
952 cdr.File = cdr.Param[0];
953 cdr.Channel = cdr.Param[1];
957 case CdlSetmode + CMD_WHILE_NOT_READY:
958 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
959 cdr.Mode = cdr.Param[0];
963 case CdlGetparam + CMD_WHILE_NOT_READY:
964 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
966 cdr.Result[1] = cdr.Mode;
968 cdr.Result[3] = cdr.File;
969 cdr.Result[4] = cdr.Channel;
973 if (cdr.LocL[0] == LOCL_INVALID) {
978 memcpy(cdr.Result, cdr.LocL, 8);
983 memcpy(&cdr.Result, &cdr.subq, 8);
986 case CdlReadT: // SetSession?
988 second_resp_time = cdReadTime * 290 / 4;
992 case CdlReadT + CMD_PART2:
998 if (CDR_getTN(cdr.ResultTN) == -1) {
999 cdr.Stat = DiskError;
1000 cdr.Result[0] |= STATUS_ERROR;
1002 cdr.Stat = Acknowledge;
1003 cdr.Result[1] = itob(cdr.ResultTN[0]);
1004 cdr.Result[2] = itob(cdr.ResultTN[1]);
1009 cdr.Track = btoi(cdr.Param[0]);
1011 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1012 cdr.Stat = DiskError;
1013 cdr.Result[0] |= STATUS_ERROR;
1015 cdr.Stat = Acknowledge;
1016 cdr.Result[0] = cdr.StatP;
1017 cdr.Result[1] = itob(cdr.ResultTD[2]);
1018 cdr.Result[2] = itob(cdr.ResultTD[1]);
1019 /* According to Nocash's documentation, the function doesn't care about ff.
1020 * This can be seen also in Mednafen's implementation. */
1021 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1029 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1031 seekTime = cdrSeekTime(cdr.SetSector);
1032 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1034 Crusaders of Might and Magic = 0.5x-4x
1035 - fix cutscene speech start
1037 Eggs of Steel = 2x-?
1041 - fix cutscene speech
1046 second_resp_time = cdReadTime + seekTime;
1050 case CdlSeekL + CMD_PART2:
1051 case CdlSeekP + CMD_PART2:
1052 SetPlaySeekRead(cdr.StatP, 0);
1053 cdr.Result[0] = cdr.StatP;
1054 cdr.Stat = Complete;
1056 Find_CurTrack(cdr.SetSectorPlay);
1057 read_ok = ReadTrack(cdr.SetSectorPlay);
1058 if (read_ok && (buf = CDR_getBuffer()))
1059 memcpy(cdr.LocL, buf, 8);
1060 UpdateSubq(cdr.SetSectorPlay);
1061 cdr.TrackChanged = FALSE;
1065 case CdlTest + CMD_WHILE_NOT_READY:
1066 switch (cdr.Param[0]) {
1067 case 0x20: // System Controller ROM Version
1069 memcpy(cdr.Result, Test20, 4);
1073 memcpy(cdr.Result, Test22, 4);
1075 case 0x23: case 0x24:
1077 memcpy(cdr.Result, Test23, 4);
1083 second_resp_time = 20480;
1086 case CdlID + CMD_PART2:
1088 cdr.Result[0] = cdr.StatP;
1093 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1094 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1095 cdr.Result[1] = 0xc0;
1099 cdr.Result[1] |= 0x10;
1100 if (CdromId[0] == '\0')
1101 cdr.Result[1] |= 0x80;
1103 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1105 /* This adds the string "PCSX" in Playstation bios boot screen */
1106 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1107 cdr.Stat = Complete;
1111 case CdlInit + CMD_WHILE_NOT_READY:
1114 SetPlaySeekRead(cdr.StatP, 0);
1115 // yes, it really sets STATUS_SHELLOPEN
1116 cdr.StatP |= STATUS_SHELLOPEN;
1117 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1123 case CdlGetQ + CMD_WHILE_NOT_READY:
1127 case CdlReadToc + CMD_WHILE_NOT_READY:
1128 cdr.LocL[0] = LOCL_INVALID;
1129 second_resp_time = cdReadTime * 180 / 4;
1133 case CdlReadToc + CMD_PART2:
1134 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1135 cdr.Stat = Complete;
1140 if (cdr.Reading && !cdr.SetlocPending)
1143 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1145 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1146 // Read* acts as play for cdda tracks in cdda mode
1150 if (cdr.SetlocPending) {
1151 seekTime = cdrSeekTime(cdr.SetSector);
1152 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1153 cdr.SetlocPending = 0;
1156 cdr.FirstSector = 1;
1158 // Fighting Force 2 - update subq time immediately
1160 UpdateSubq(cdr.SetSectorPlay);
1161 cdr.LocL[0] = LOCL_INVALID;
1162 cdr.SubqForwardSectors = 1;
1164 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1166 if (Config.hacks.cdr_read_timing)
1167 cycles = cdrAlignTimingHack(cycles);
1168 CDRPLAYREAD_INT(cycles, 1);
1170 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1176 error = ERROR_INVALIDCMD;
1180 CDR_LOG_I("cmd %02x error %02x\n", Cmd, error);
1182 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1183 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1184 cdr.Stat = DiskError;
1188 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1189 cdr.DriveState = DRIVESTATE_STANDBY;
1190 cdr.StatP |= STATUS_ROTATING;
1193 if (second_resp_time) {
1194 cdr.CmdInProgress = Cmd | 0x100;
1195 CDR_INT(second_resp_time);
1197 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1198 cdr.CmdInProgress = cdr.Cmd;
1199 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1206 #define ssat32_to_16(v) \
1207 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1209 #define ssat32_to_16(v) do { \
1210 if (v < -32768) v = -32768; \
1211 else if (v > 32767) v = 32767; \
1215 static void cdrPrepCdda(s16 *buf, int samples)
1217 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1219 for (i = 0; i < samples; i++) {
1220 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1221 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1226 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1229 int ll = cdr.AttenuatorLeftToLeft;
1230 int lr = cdr.AttenuatorLeftToRight;
1231 int rl = cdr.AttenuatorRightToLeft;
1232 int rr = cdr.AttenuatorRightToRight;
1234 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1237 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1241 for (i = 0; i < samples; i++) {
1244 l = (l * ll + r * rl) >> 7;
1245 r = (r * rr + l * lr) >> 7;
1253 for (i = 0; i < samples; i++) {
1255 l = l * (ll + rl) >> 7;
1256 //r = r * (rr + lr) >> 7;
1264 static void cdrReadInterruptSetResult(unsigned char result)
1267 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1268 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1269 cdr.CmdInProgress, cdr.Stat);
1270 cdr.Irq1Pending = result;
1274 cdr.Result[0] = result;
1275 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1279 static void cdrUpdateTransferBuf(const u8 *buf)
1283 memcpy(cdr.Transfer, buf, DATA_SIZE);
1284 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1285 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1286 if (cdr.FifoOffset < 2048 + 12)
1287 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1290 static void cdrReadInterrupt(void)
1292 u8 *buf = NULL, *hdr;
1296 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1297 msfiAdd(subqPos, cdr.SubqForwardSectors);
1298 UpdateSubq(subqPos);
1299 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1300 cdr.SubqForwardSectors++;
1301 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1305 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1306 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1308 read_ok = ReadTrack(cdr.SetSectorPlay);
1310 buf = CDR_getBuffer();
1315 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1316 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1319 memcpy(cdr.LocL, buf, 8);
1321 if (!cdr.Stat && !cdr.Irq1Pending)
1322 cdrUpdateTransferBuf(buf);
1324 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1326 // Firemen 2: Multi-XA files - briefings, cutscenes
1327 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1329 cdr.Channel = hdr[1];
1333 * Skips playing on channel 255.
1334 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1335 * TODO : Check if this is the proper behaviour.
1337 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1338 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1340 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1341 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1342 cdr.FirstSector = 0;
1344 else cdr.FirstSector = -1;
1349 Croc 2: $40 - only FORM1 (*)
1350 Judge Dredd: $C8 - only FORM1 (*)
1351 Sim Theme Park - no adpcm at all (zero)
1354 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1355 cdrReadInterruptSetResult(cdr.StatP);
1357 msfiAdd(cdr.SetSectorPlay, 1);
1359 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1368 bit 5 - 1 result ready
1370 bit 7 - 1 command being processed
1373 unsigned char cdrRead0(void) {
1374 if (cdr.ResultReady)
1379 cdr.Ctrl |= 0x40; // data fifo not empty
1381 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1384 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1386 return psxHu8(0x1800) = cdr.Ctrl;
1389 void cdrWrite0(unsigned char rt) {
1390 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1392 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1395 unsigned char cdrRead1(void) {
1396 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1397 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1401 if (cdr.ResultP == cdr.ResultC)
1402 cdr.ResultReady = 0;
1404 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1406 return psxHu8(0x1801);
1409 void cdrWrite1(unsigned char rt) {
1410 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1411 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1413 switch (cdr.Ctrl & 3) {
1417 cdr.AttenuatorRightToRightT = rt;
1423 #ifdef CDR_LOG_CMD_IRQ
1424 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1427 SysPrintf(" Param[%d] = {", cdr.ParamC);
1428 for (i = 0; i < cdr.ParamC; i++)
1429 SysPrintf(" %x,", cdr.Param[i]);
1436 cdr.ResultReady = 0;
1439 if (!cdr.CmdInProgress) {
1440 cdr.CmdInProgress = rt;
1441 // should be something like 12k + controller delays
1445 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1446 rt, cdr.Cmd, cdr.CmdInProgress);
1447 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1448 cdr.CmdInProgress = rt;
1454 unsigned char cdrRead2(void) {
1455 unsigned char ret = cdr.Transfer[0x920];
1457 if (cdr.FifoOffset < cdr.FifoSize)
1458 ret = cdr.Transfer[cdr.FifoOffset++];
1460 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1462 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1466 void cdrWrite2(unsigned char rt) {
1467 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1468 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1470 switch (cdr.Ctrl & 3) {
1472 if (cdr.ParamC < 8) // FIXME: size and wrapping
1473 cdr.Param[cdr.ParamC++] = rt;
1480 cdr.AttenuatorLeftToLeftT = rt;
1483 cdr.AttenuatorRightToLeftT = rt;
1488 unsigned char cdrRead3(void) {
1490 psxHu8(0x1803) = cdr.Stat | 0xE0;
1492 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1494 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1495 return psxHu8(0x1803);
1498 void cdrWrite3(unsigned char rt) {
1499 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1500 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1502 switch (cdr.Ctrl & 3) {
1506 if (cdr.Stat & rt) {
1507 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1508 + psxRegs.intCycle[PSXINT_CDR].cycle;
1509 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1510 #ifdef CDR_LOG_CMD_IRQ
1511 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1512 !!pending, cdr.CmdInProgress,
1513 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1515 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1516 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1519 if (cdr.CmdInProgress) {
1520 c = 2048 - (psxRegs.cycle - nextCycle);
1521 c = MAX_VALUE(c, 512);
1532 cdr.AttenuatorLeftToRightT = rt;
1536 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1537 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1538 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1539 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1545 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1546 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1548 else if (rt & 0x80) {
1549 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1550 case MODE_SIZE_2328:
1552 cdr.FifoOffset = 12;
1553 cdr.FifoSize = 2048 + 12;
1556 case MODE_SIZE_2340:
1559 cdr.FifoSize = 2340;
1563 else if (!(rt & 0xc0))
1564 cdr.FifoOffset = DATA_SIZE; // fifo empty
1567 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1573 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1574 if (cdr.FifoOffset == 0) {
1576 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1581 switch (chcr & 0x71000000) {
1583 ptr = (u8 *)PSXM(madr);
1584 if (ptr == INVALID_PTR) {
1585 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1589 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1592 GS CDX: Enhancement CD crash
1595 - Spams DMA3 and gets buffer overrun
1597 size = DATA_SIZE - cdr.FifoOffset;
1602 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1603 cdr.FifoOffset += size;
1605 if (size < cdsize) {
1606 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1607 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1609 psxCpu->Clear(madr, cdsize / 4);
1611 CDRDMA_INT((cdsize/4) * 24);
1613 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1615 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1616 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1620 psxRegs.cycle += (cdsize/4) * 24 - 20;
1625 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1629 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1633 void cdrDmaInterrupt(void)
1635 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1637 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1642 static void getCdInfo(void)
1646 CDR_getTN(cdr.ResultTN);
1647 CDR_getTD(0, cdr.SetSectorEnd);
1648 tmp = cdr.SetSectorEnd[0];
1649 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1650 cdr.SetSectorEnd[2] = tmp;
1654 memset(&cdr, 0, sizeof(cdr));
1660 cdr.FifoOffset = DATA_SIZE; // fifo empty
1661 if (CdromId[0] == '\0') {
1662 cdr.DriveState = DRIVESTATE_STOPPED;
1666 cdr.DriveState = DRIVESTATE_STANDBY;
1667 cdr.StatP = STATUS_ROTATING;
1670 // BIOS player - default values
1671 cdr.AttenuatorLeftToLeft = 0x80;
1672 cdr.AttenuatorLeftToRight = 0x00;
1673 cdr.AttenuatorRightToLeft = 0x00;
1674 cdr.AttenuatorRightToRight = 0x80;
1679 int cdrFreeze(void *f, int Mode) {
1683 if (Mode == 0 && !Config.Cdda)
1686 cdr.freeze_ver = 0x63647202;
1687 gzfreeze(&cdr, sizeof(cdr));
1690 cdr.ParamP = cdr.ParamC;
1691 tmp = cdr.FifoOffset;
1694 gzfreeze(&tmp, sizeof(tmp));
1699 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1700 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1701 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1702 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1704 // read right sub data
1705 tmpp[0] = btoi(cdr.Prev[0]);
1706 tmpp[1] = btoi(cdr.Prev[1]);
1707 tmpp[2] = btoi(cdr.Prev[2]);
1712 if (cdr.freeze_ver < 0x63647202)
1713 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1715 Find_CurTrack(cdr.SetSectorPlay);
1717 CDR_play(cdr.SetSectorPlay);
1718 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1719 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 1);
1722 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1723 // old versions did not latch Reg2, have to fixup..
1724 if (cdr.Reg2 == 0) {
1725 SysPrintf("cdrom: fixing up old savestate\n");
1728 // also did not save Attenuator..
1729 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1730 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1732 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1740 void LidInterrupt(void) {
1742 cdrLidSeekInterrupt();