1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
18 ***************************************************************************/
21 * Handles all CD-ROM registers and functions.
29 #include "arm_features.h"
33 #define CDR_LOG SysPrintf
38 #define CDR_LOG_I SysPrintf
40 #define CDR_LOG_I(fmt, ...) \
41 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
44 #define CDR_LOG_IO SysPrintf
46 #define CDR_LOG_IO(...)
48 //#define CDR_LOG_CMD_IRQ
51 // unused members maintain savesate compatibility
52 unsigned char unused0;
53 unsigned char unused1;
55 unsigned char unused2;
61 unsigned char Transfer[DATA_SIZE];
65 unsigned char Relative[3];
66 unsigned char Absolute[3];
68 unsigned char TrackChanged;
69 unsigned char ReportDelay;
70 unsigned char unused3[2];
71 unsigned int freeze_ver;
73 unsigned char Prev[4];
74 unsigned char Param[8];
75 unsigned char Result[16];
79 unsigned char ResultC;
80 unsigned char ResultP;
81 unsigned char ResultReady;
83 unsigned char SubqForwardSectors;
84 unsigned char SetlocPending;
87 unsigned char ResultTN[6];
88 unsigned char ResultTD[4];
89 unsigned char SetSectorPlay[4];
90 unsigned char SetSectorEnd[4];
91 unsigned char SetSector[4];
95 int Mode, File, Channel;
96 unsigned char LocL[8];
116 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
117 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
118 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
119 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
121 static s16 read_buf[CD_FRAMESIZE_RAW/2];
123 /* CD-ROM magic numbers */
124 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
129 #define CdlBackward 5
137 #define CdlSetfilter 13
138 #define CdlSetmode 14
139 #define CdlGetparam 15
140 #define CdlGetlocL 16
141 #define CdlGetlocP 17
147 #define CdlSetclock 23
148 #define CdlGetclock 24
154 #define CdlReadToc 30
156 #ifdef CDR_LOG_CMD_IRQ
157 static const char * const CmdName[0x100] = {
158 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
159 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
160 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
161 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
162 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
163 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
164 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
165 "CdlInit", NULL, "CDlReadToc", NULL
169 unsigned char Test04[] = { 0 };
170 unsigned char Test05[] = { 0 };
171 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
172 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
173 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
179 #define Acknowledge 3
184 #define MODE_SPEED (1<<7) // 0x80
185 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
186 #define MODE_SIZE_2340 (1<<5) // 0x20
187 #define MODE_SIZE_2328 (1<<4) // 0x10
188 #define MODE_SIZE_2048 (0<<4) // 0x00
189 #define MODE_SF (1<<3) // 0x08 channel on/off
190 #define MODE_REPORT (1<<2) // 0x04
191 #define MODE_AUTOPAUSE (1<<1) // 0x02
192 #define MODE_CDDA (1<<0) // 0x01
195 #define STATUS_PLAY (1<<7) // 0x80
196 #define STATUS_SEEK (1<<6) // 0x40
197 #define STATUS_READ (1<<5) // 0x20
198 #define STATUS_SHELLOPEN (1<<4) // 0x10
199 #define STATUS_UNKNOWN3 (1<<3) // 0x08
200 #define STATUS_UNKNOWN2 (1<<2) // 0x04
201 #define STATUS_ROTATING (1<<1) // 0x02
202 #define STATUS_ERROR (1<<0) // 0x01
205 #define ERROR_NOTREADY (1<<7) // 0x80
206 #define ERROR_INVALIDCMD (1<<6) // 0x40
207 #define ERROR_INVALIDARG (1<<5) // 0x20
209 // 1x = 75 sectors per second
210 // PSXCLK = 1 sec in the ps
211 // so (PSXCLK / 75) = cdr read time (linuzappz)
212 #define cdReadTime (PSXCLK / 75)
214 #define LOCL_INVALID 0xff
215 #define SUBQ_FORWARD_SECTORS 2u
218 DRIVESTATE_STANDBY = 0, // pause, play, read
220 DRIVESTATE_RESCAN_CD,
221 DRIVESTATE_PREPARE_CD,
225 static struct CdrStat stat;
227 static unsigned int msf2sec(const u8 *msf) {
228 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
231 // for that weird psemu API..
232 static unsigned int fsm2sec(const u8 *msf) {
233 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
236 static void sec2msf(unsigned int s, u8 *msf) {
237 msf[0] = s / 75 / 60;
238 s = s - msf[0] * 75 * 60;
245 #define CDR_INT(eCycle) { \
246 psxRegs.interrupt |= (1 << PSXINT_CDR); \
247 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
248 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
249 new_dyna_set_event(PSXINT_CDR, eCycle); \
252 // cdrPlayReadInterrupt
253 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
255 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
257 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
259 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
260 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
261 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
264 // cdrLidSeekInterrupt
265 #define CDRLID_INT(eCycle) { \
266 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
267 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
268 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
269 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
272 #define StopReading() { \
274 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
277 #define StopCdda() { \
278 if (cdr.Play && !Config.Cdda) CDR_stop(); \
280 cdr.FastForward = 0; \
281 cdr.FastBackward = 0; \
284 #define SetPlaySeekRead(x, f) { \
285 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
289 #define SetResultSize(size) { \
291 cdr.ResultC = size; \
292 cdr.ResultReady = 1; \
295 static void setIrq(int log_cmd)
297 if (cdr.Stat & cdr.Reg2)
298 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
300 #ifdef CDR_LOG_CMD_IRQ
304 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
305 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
306 for (i = 0; i < cdr.ResultC; i++)
307 SysPrintf("%02x ", cdr.Result[i]);
313 // timing used in this function was taken from tests on real hardware
314 // (yes it's slow, but you probably don't want to modify it)
315 void cdrLidSeekInterrupt(void)
317 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
319 switch (cdr.DriveState) {
321 case DRIVESTATE_STANDBY:
324 SetPlaySeekRead(cdr.StatP, 0);
326 if (CDR_getStatus(&stat) == -1)
329 if (stat.Status & STATUS_SHELLOPEN)
331 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
332 cdr.DriveState = DRIVESTATE_LID_OPEN;
337 case DRIVESTATE_LID_OPEN:
338 if (CDR_getStatus(&stat) == -1)
339 stat.Status &= ~STATUS_SHELLOPEN;
342 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
343 cdr.StatP |= STATUS_SHELLOPEN;
345 // could generate error irq here, but real hardware
346 // only sometimes does that
347 // (not done when lots of commands are sent?)
349 CDRLID_INT(cdReadTime * 30);
352 else if (cdr.StatP & STATUS_ROTATING) {
353 cdr.StatP &= ~STATUS_ROTATING;
355 else if (!(stat.Status & STATUS_SHELLOPEN)) {
359 // cdr.StatP STATUS_SHELLOPEN is "sticky"
360 // and is only cleared by CdlNop
362 cdr.DriveState = DRIVESTATE_RESCAN_CD;
363 CDRLID_INT(cdReadTime * 105);
368 CDRLID_INT(cdReadTime * 3);
371 case DRIVESTATE_RESCAN_CD:
372 cdr.StatP |= STATUS_ROTATING;
373 cdr.DriveState = DRIVESTATE_PREPARE_CD;
375 // this is very long on real hardware, over 6 seconds
376 // make it a bit faster here...
377 CDRLID_INT(cdReadTime * 150);
380 case DRIVESTATE_PREPARE_CD:
381 if (cdr.StatP & STATUS_SEEK) {
382 SetPlaySeekRead(cdr.StatP, 0);
383 cdr.DriveState = DRIVESTATE_STANDBY;
386 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
387 CDRLID_INT(cdReadTime * 26);
393 static void Find_CurTrack(const u8 *time)
397 current = msf2sec(time);
399 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
400 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
401 sect = fsm2sec(cdr.ResultTD);
402 if (sect - current >= 150)
407 static void generate_subq(const u8 *time)
409 unsigned char start[3], next[3];
410 unsigned int this_s, start_s, next_s, pregap;
413 CDR_getTD(cdr.CurTrack, start);
414 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
416 CDR_getTD(cdr.CurTrack + 1, next);
419 // last track - cd size
421 next[0] = cdr.SetSectorEnd[2];
422 next[1] = cdr.SetSectorEnd[1];
423 next[2] = cdr.SetSectorEnd[0];
426 this_s = msf2sec(time);
427 start_s = fsm2sec(start);
428 next_s = fsm2sec(next);
430 cdr.TrackChanged = FALSE;
432 if (next_s - this_s < pregap) {
433 cdr.TrackChanged = TRUE;
440 relative_s = this_s - start_s;
441 if (relative_s < 0) {
443 relative_s = -relative_s;
445 sec2msf(relative_s, cdr.subq.Relative);
447 cdr.subq.Track = itob(cdr.CurTrack);
448 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
449 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
450 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
451 cdr.subq.Absolute[0] = itob(time[0]);
452 cdr.subq.Absolute[1] = itob(time[1]);
453 cdr.subq.Absolute[2] = itob(time[2]);
456 static int ReadTrack(const u8 *time)
458 unsigned char tmp[3];
461 tmp[0] = itob(time[0]);
462 tmp[1] = itob(time[1]);
463 tmp[2] = itob(time[2]);
465 if (memcmp(cdr.Prev, tmp, 3) == 0)
468 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
470 read_ok = CDR_readTrack(tmp);
472 memcpy(cdr.Prev, tmp, 3);
476 static void UpdateSubq(const u8 *time)
478 const struct SubQ *subq;
484 subq = (struct SubQ *)CDR_getBufferSub(MSF2SECT(time[0], time[1], time[2]));
485 if (subq != NULL && cdr.CurTrack == 1) {
486 crc = calcCrc((u8 *)subq + 12, 10);
487 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
488 cdr.subq.Track = subq->TrackNumber;
489 cdr.subq.Index = subq->IndexNumber;
490 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
491 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
494 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
495 time[0], time[1], time[2]);
502 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
503 cdr.subq.Track, cdr.subq.Index,
504 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
505 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
508 static void cdrPlayInterrupt_Autopause()
511 boolean abs_lev_chselect;
514 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
515 CDR_LOG( "CDDA STOP\n" );
517 // Magic the Gathering
518 // - looping territory cdda
521 //cdr.ResultReady = 1;
522 //cdr.Stat = DataReady;
524 setIrq(0x1000); // 0x1000 just for logging purposes
527 SetPlaySeekRead(cdr.StatP, 0);
529 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
530 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
532 cdr.Result[0] = cdr.StatP;
533 cdr.Result[1] = cdr.subq.Track;
534 cdr.Result[2] = cdr.subq.Index;
536 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
538 /* 8 is a hack. For accuracy, it should be 588. */
539 for (i = 0; i < 8; i++)
541 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
543 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
544 abs_lev_max |= abs_lev_chselect << 15;
546 if (cdr.subq.Absolute[2] & 0x10) {
547 cdr.Result[3] = cdr.subq.Relative[0];
548 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
549 cdr.Result[5] = cdr.subq.Relative[2];
552 cdr.Result[3] = cdr.subq.Absolute[0];
553 cdr.Result[4] = cdr.subq.Absolute[1];
554 cdr.Result[5] = cdr.subq.Absolute[2];
557 cdr.Result[6] = abs_lev_max >> 0;
558 cdr.Result[7] = abs_lev_max >> 8;
560 // Rayman: Logo freeze (resultready + dataready)
562 cdr.Stat = DataReady;
573 static int cdrSeekTime(unsigned char *target)
575 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
576 int pausePenalty, seekTime = abs(diff) * (cdReadTime / 2000);
577 seekTime = MAX_VALUE(seekTime, 20000);
579 // need this stupidly long penalty or else Spyro2 intro desyncs
580 pausePenalty = (s32)(psxRegs.cycle - cdr.LastReadCycles) > cdReadTime * 4 ? cdReadTime * 25 : 0;
581 seekTime += pausePenalty;
583 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
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)
636 cdr.LastReadCycles = psxRegs.cycle;
643 if (!cdr.Play) return;
645 CDR_LOG( "CDDA - %d:%d:%d\n",
646 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
648 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
649 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
651 SetPlaySeekRead(cdr.StatP, 0);
652 cdr.TrackChanged = TRUE;
655 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
658 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
659 cdrPlayInterrupt_Autopause();
661 if (!cdr.Muted && !Config.Cdda) {
662 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
663 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
664 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
668 msfiAdd(cdr.SetSectorPlay, 1);
670 // update for CdlGetlocP/autopause
671 generate_subq(cdr.SetSectorPlay);
673 CDRPLAYREAD_INT(cdReadTime, 0);
676 #define CMD_PART2 0x100
677 #define CMD_WHILE_NOT_READY 0x200
679 void cdrInterrupt(void) {
680 int start_rotating = 0;
682 u32 cycles, seekTime = 0;
683 u32 second_resp_time = 0;
693 CDR_LOG_I("cmd %02x with irqstat %x\n",
694 cdr.CmdInProgress, cdr.Stat);
697 if (cdr.Irq1Pending) {
698 // hand out the "newest" sector, according to nocash
699 cdrUpdateTransferBuf(CDR_getBuffer());
700 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
701 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
702 cdr.CmdInProgress, cdr.Irq1Pending);
704 cdr.Result[0] = cdr.Irq1Pending;
705 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
713 cdr.Result[0] = cdr.StatP;
714 cdr.Stat = Acknowledge;
716 Cmd = cdr.CmdInProgress;
717 cdr.CmdInProgress = 0;
726 switch (cdr.DriveState) {
727 case DRIVESTATE_PREPARE_CD:
729 // Syphon filter 2 expects commands to work shortly after it sees
730 // STATUS_ROTATING, so give up trying to emulate the startup seq
731 cdr.DriveState = DRIVESTATE_STANDBY;
732 cdr.StatP &= ~STATUS_SEEK;
733 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
737 case DRIVESTATE_LID_OPEN:
738 case DRIVESTATE_RESCAN_CD:
739 // no disk or busy with the initial scan, allowed cmds are limited
740 not_ready = CMD_WHILE_NOT_READY;
744 switch (Cmd | not_ready) {
746 case CdlNop + CMD_WHILE_NOT_READY:
747 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
748 cdr.StatP &= ~STATUS_SHELLOPEN;
752 case CdlSetloc + CMD_WHILE_NOT_READY:
753 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
755 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
756 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))
758 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
759 if (++cdr.errorRetryhack > 100)
761 error = ERROR_INVALIDARG;
766 for (i = 0; i < 3; i++)
767 set_loc[i] = btoi(cdr.Param[i]);
768 memcpy(cdr.SetSector, set_loc, 3);
769 cdr.SetSector[3] = 0;
770 cdr.SetlocPending = 1;
771 cdr.errorRetryhack = 0;
780 cdr.FastBackward = 0;
784 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
786 if (ParamC != 0 && cdr.Param[0] != 0) {
787 int track = btoi( cdr.Param[0] );
789 if (track <= cdr.ResultTN[1])
790 cdr.CurTrack = track;
792 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
794 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
795 for (i = 0; i < 3; i++)
796 set_loc[i] = cdr.ResultTD[2 - i];
797 seekTime = cdrSeekTime(set_loc);
798 memcpy(cdr.SetSectorPlay, set_loc, 3);
801 else if (cdr.SetlocPending) {
802 seekTime = cdrSeekTime(cdr.SetSector);
803 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
806 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
807 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
809 cdr.SetlocPending = 0;
812 Rayman: detect track changes
815 Twisted Metal 2: skip PREGAP + starting accurate SubQ
816 - plays tracks without retry play
818 Wild 9: skip PREGAP + starting accurate SubQ
819 - plays tracks without retry play
821 Find_CurTrack(cdr.SetSectorPlay);
822 generate_subq(cdr.SetSectorPlay);
823 cdr.LocL[0] = LOCL_INVALID;
824 cdr.SubqForwardSectors = 1;
825 cdr.TrackChanged = FALSE;
827 cdr.ReportDelay = 60;
830 CDR_play(cdr.SetSectorPlay);
832 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
834 // BIOS player - set flag again
837 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
842 // TODO: error 80 if stopped
845 // GameShark CD Player: Calls 2x + Play 2x
847 cdr.FastBackward = 0;
853 // GameShark CD Player: Calls 2x + Play 2x
854 cdr.FastBackward = 1;
859 if (cdr.DriveState != DRIVESTATE_STOPPED) {
860 error = ERROR_INVALIDARG;
863 second_resp_time = cdReadTime * 125 / 2;
867 case CdlStandby + CMD_PART2:
873 // grab time for current track
874 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
876 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
877 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
878 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
883 SetPlaySeekRead(cdr.StatP, 0);
884 cdr.StatP &= ~STATUS_ROTATING;
885 cdr.LocL[0] = LOCL_INVALID;
887 second_resp_time = 0x800;
888 if (cdr.DriveState == DRIVESTATE_STANDBY)
889 second_resp_time = cdReadTime * 30 / 2;
891 cdr.DriveState = DRIVESTATE_STOPPED;
894 case CdlStop + CMD_PART2:
902 Gundam Battle Assault 2: much slower (*)
903 - Fixes boot, gameplay
905 Hokuto no Ken 2: slower
906 - Fixes intro + subtitles
908 InuYasha - Feudal Fairy Tale: slower
911 /* Gameblabla - Tightening the timings (as taken from Duckstation).
912 * The timings from Duckstation are based upon hardware tests.
913 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
914 * seems to be timing sensitive as it can depend on the CPU's clock speed.
916 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
918 second_resp_time = 7000;
922 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
924 SetPlaySeekRead(cdr.StatP, 0);
927 case CdlPause + CMD_PART2:
932 case CdlReset + CMD_WHILE_NOT_READY:
935 SetPlaySeekRead(cdr.StatP, 0);
936 cdr.LocL[0] = LOCL_INVALID;
938 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
939 second_resp_time = not_ready ? 70000 : 4100000;
943 case CdlReset + CMD_PART2:
944 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
957 cdr.File = cdr.Param[0];
958 cdr.Channel = cdr.Param[1];
962 case CdlSetmode + CMD_WHILE_NOT_READY:
963 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
964 cdr.Mode = cdr.Param[0];
968 case CdlGetparam + CMD_WHILE_NOT_READY:
969 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
971 cdr.Result[1] = cdr.Mode;
973 cdr.Result[3] = cdr.File;
974 cdr.Result[4] = cdr.Channel;
978 if (cdr.LocL[0] == LOCL_INVALID) {
983 memcpy(cdr.Result, cdr.LocL, 8);
988 memcpy(&cdr.Result, &cdr.subq, 8);
991 case CdlReadT: // SetSession?
993 second_resp_time = cdReadTime * 290 / 4;
997 case CdlReadT + CMD_PART2:
1003 if (CDR_getTN(cdr.ResultTN) == -1) {
1004 cdr.Stat = DiskError;
1005 cdr.Result[0] |= STATUS_ERROR;
1007 cdr.Stat = Acknowledge;
1008 cdr.Result[1] = itob(cdr.ResultTN[0]);
1009 cdr.Result[2] = itob(cdr.ResultTN[1]);
1014 cdr.Track = btoi(cdr.Param[0]);
1016 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1017 cdr.Stat = DiskError;
1018 cdr.Result[0] |= STATUS_ERROR;
1020 cdr.Stat = Acknowledge;
1021 cdr.Result[0] = cdr.StatP;
1022 cdr.Result[1] = itob(cdr.ResultTD[2]);
1023 cdr.Result[2] = itob(cdr.ResultTD[1]);
1024 /* According to Nocash's documentation, the function doesn't care about ff.
1025 * This can be seen also in Mednafen's implementation. */
1026 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1034 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1036 seekTime = cdrSeekTime(cdr.SetSector);
1037 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1039 Crusaders of Might and Magic = 0.5x-4x
1040 - fix cutscene speech start
1042 Eggs of Steel = 2x-?
1046 - fix cutscene speech
1051 second_resp_time = cdReadTime + seekTime;
1055 case CdlSeekL + CMD_PART2:
1056 case CdlSeekP + CMD_PART2:
1057 SetPlaySeekRead(cdr.StatP, 0);
1058 cdr.Result[0] = cdr.StatP;
1059 cdr.Stat = Complete;
1061 Find_CurTrack(cdr.SetSectorPlay);
1062 read_ok = ReadTrack(cdr.SetSectorPlay);
1063 if (read_ok && (buf = CDR_getBuffer()))
1064 memcpy(cdr.LocL, buf, 8);
1065 UpdateSubq(cdr.SetSectorPlay);
1066 cdr.TrackChanged = FALSE;
1070 case CdlTest + CMD_WHILE_NOT_READY:
1071 switch (cdr.Param[0]) {
1072 case 0x20: // System Controller ROM Version
1074 memcpy(cdr.Result, Test20, 4);
1078 memcpy(cdr.Result, Test22, 4);
1080 case 0x23: case 0x24:
1082 memcpy(cdr.Result, Test23, 4);
1088 second_resp_time = 20480;
1091 case CdlID + CMD_PART2:
1093 cdr.Result[0] = cdr.StatP;
1098 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1099 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1100 cdr.Result[1] = 0xc0;
1104 cdr.Result[1] |= 0x10;
1105 if (CdromId[0] == '\0')
1106 cdr.Result[1] |= 0x80;
1108 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1110 /* This adds the string "PCSX" in Playstation bios boot screen */
1111 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1112 cdr.Stat = Complete;
1116 case CdlInit + CMD_WHILE_NOT_READY:
1119 SetPlaySeekRead(cdr.StatP, 0);
1120 // yes, it really sets STATUS_SHELLOPEN
1121 cdr.StatP |= STATUS_SHELLOPEN;
1122 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1128 case CdlGetQ + CMD_WHILE_NOT_READY:
1132 case CdlReadToc + CMD_WHILE_NOT_READY:
1133 cdr.LocL[0] = LOCL_INVALID;
1134 second_resp_time = cdReadTime * 180 / 4;
1138 case CdlReadToc + CMD_PART2:
1139 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1140 cdr.Stat = Complete;
1145 if (cdr.Reading && !cdr.SetlocPending)
1148 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1150 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1151 // Read* acts as play for cdda tracks in cdda mode
1155 if (cdr.SetlocPending) {
1156 seekTime = cdrSeekTime(cdr.SetSector);
1157 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1158 cdr.SetlocPending = 0;
1161 cdr.FirstSector = 1;
1163 // Fighting Force 2 - update subq time immediately
1165 UpdateSubq(cdr.SetSectorPlay);
1166 cdr.LocL[0] = LOCL_INVALID;
1167 cdr.SubqForwardSectors = 1;
1169 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1171 if (Config.hacks.cdr_read_timing)
1172 cycles = cdrAlignTimingHack(cycles);
1173 CDRPLAYREAD_INT(cycles, 1);
1175 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1181 error = ERROR_INVALIDCMD;
1185 CDR_LOG_I("cmd %02x error %02x\n", Cmd, error);
1187 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1188 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1189 cdr.Stat = DiskError;
1193 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1194 cdr.DriveState = DRIVESTATE_STANDBY;
1195 cdr.StatP |= STATUS_ROTATING;
1198 if (second_resp_time) {
1199 cdr.CmdInProgress = Cmd | 0x100;
1200 CDR_INT(second_resp_time);
1202 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1203 cdr.CmdInProgress = cdr.Cmd;
1204 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1211 #define ssat32_to_16(v) \
1212 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1214 #define ssat32_to_16(v) do { \
1215 if (v < -32768) v = -32768; \
1216 else if (v > 32767) v = 32767; \
1220 static void cdrPrepCdda(s16 *buf, int samples)
1222 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1224 for (i = 0; i < samples; i++) {
1225 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1226 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1231 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1234 int ll = cdr.AttenuatorLeftToLeft;
1235 int lr = cdr.AttenuatorLeftToRight;
1236 int rl = cdr.AttenuatorRightToLeft;
1237 int rr = cdr.AttenuatorRightToRight;
1239 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1242 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1246 for (i = 0; i < samples; i++) {
1249 l = (l * ll + r * rl) >> 7;
1250 r = (r * rr + l * lr) >> 7;
1258 for (i = 0; i < samples; i++) {
1260 l = l * (ll + rl) >> 7;
1261 //r = r * (rr + lr) >> 7;
1269 static void cdrReadInterruptSetResult(unsigned char result)
1272 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1273 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1274 cdr.CmdInProgress, cdr.Stat);
1275 cdr.Irq1Pending = result;
1279 cdr.Result[0] = result;
1280 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1284 static void cdrUpdateTransferBuf(const u8 *buf)
1288 memcpy(cdr.Transfer, buf, DATA_SIZE);
1289 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1290 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1291 if (cdr.FifoOffset < 2048 + 12)
1292 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1295 static void cdrReadInterrupt(void)
1297 u8 *buf = NULL, *hdr;
1301 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1302 msfiAdd(subqPos, cdr.SubqForwardSectors);
1303 UpdateSubq(subqPos);
1304 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1305 cdr.SubqForwardSectors++;
1306 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1310 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1311 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1313 read_ok = ReadTrack(cdr.SetSectorPlay);
1315 buf = CDR_getBuffer();
1320 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1321 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1324 memcpy(cdr.LocL, buf, 8);
1326 if (!cdr.Stat && !cdr.Irq1Pending)
1327 cdrUpdateTransferBuf(buf);
1329 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1331 // Firemen 2: Multi-XA files - briefings, cutscenes
1332 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1334 cdr.Channel = hdr[1];
1338 * Skips playing on channel 255.
1339 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1340 * TODO : Check if this is the proper behaviour.
1342 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1343 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1345 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1346 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1347 cdr.FirstSector = 0;
1349 else cdr.FirstSector = -1;
1354 Croc 2: $40 - only FORM1 (*)
1355 Judge Dredd: $C8 - only FORM1 (*)
1356 Sim Theme Park - no adpcm at all (zero)
1359 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1360 cdrReadInterruptSetResult(cdr.StatP);
1362 msfiAdd(cdr.SetSectorPlay, 1);
1364 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1373 bit 5 - 1 result ready
1375 bit 7 - 1 command being processed
1378 unsigned char cdrRead0(void) {
1379 if (cdr.ResultReady)
1384 cdr.Ctrl |= 0x40; // data fifo not empty
1386 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1389 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1391 return psxHu8(0x1800) = cdr.Ctrl;
1394 void cdrWrite0(unsigned char rt) {
1395 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1397 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1400 unsigned char cdrRead1(void) {
1401 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1402 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1406 if (cdr.ResultP == cdr.ResultC)
1407 cdr.ResultReady = 0;
1409 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1411 return psxHu8(0x1801);
1414 void cdrWrite1(unsigned char rt) {
1415 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1416 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1418 switch (cdr.Ctrl & 3) {
1422 cdr.AttenuatorRightToRightT = rt;
1428 #ifdef CDR_LOG_CMD_IRQ
1429 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1432 SysPrintf(" Param[%d] = {", cdr.ParamC);
1433 for (i = 0; i < cdr.ParamC; i++)
1434 SysPrintf(" %x,", cdr.Param[i]);
1441 cdr.ResultReady = 0;
1444 if (!cdr.CmdInProgress) {
1445 cdr.CmdInProgress = rt;
1446 // should be something like 12k + controller delays
1450 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1451 rt, cdr.Cmd, cdr.CmdInProgress);
1452 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1453 cdr.CmdInProgress = rt;
1459 unsigned char cdrRead2(void) {
1460 unsigned char ret = cdr.Transfer[0x920];
1462 if (cdr.FifoOffset < cdr.FifoSize)
1463 ret = cdr.Transfer[cdr.FifoOffset++];
1465 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1467 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1471 void cdrWrite2(unsigned char rt) {
1472 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1473 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1475 switch (cdr.Ctrl & 3) {
1477 if (cdr.ParamC < 8) // FIXME: size and wrapping
1478 cdr.Param[cdr.ParamC++] = rt;
1485 cdr.AttenuatorLeftToLeftT = rt;
1488 cdr.AttenuatorRightToLeftT = rt;
1493 unsigned char cdrRead3(void) {
1495 psxHu8(0x1803) = cdr.Stat | 0xE0;
1497 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1499 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1500 return psxHu8(0x1803);
1503 void cdrWrite3(unsigned char rt) {
1504 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1505 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1507 switch (cdr.Ctrl & 3) {
1511 if (cdr.Stat & rt) {
1512 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1513 + psxRegs.intCycle[PSXINT_CDR].cycle;
1514 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1515 #ifdef CDR_LOG_CMD_IRQ
1516 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1517 !!pending, cdr.CmdInProgress,
1518 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1520 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1521 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1524 if (cdr.CmdInProgress) {
1525 c = 2048 - (psxRegs.cycle - nextCycle);
1526 c = MAX_VALUE(c, 512);
1537 cdr.AttenuatorLeftToRightT = rt;
1541 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1542 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1543 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1544 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1550 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1551 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1553 else if (rt & 0x80) {
1554 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1555 case MODE_SIZE_2328:
1557 cdr.FifoOffset = 12;
1558 cdr.FifoSize = 2048 + 12;
1561 case MODE_SIZE_2340:
1564 cdr.FifoSize = 2340;
1568 else if (!(rt & 0xc0))
1569 cdr.FifoOffset = DATA_SIZE; // fifo empty
1572 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1573 u32 cdsize, max_words;
1578 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1579 if (cdr.FifoOffset == 0) {
1581 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1586 switch (chcr & 0x71000000) {
1588 ptr = getDmaRam(madr, &max_words);
1589 if (ptr == INVALID_PTR) {
1590 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1594 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1597 GS CDX: Enhancement CD crash
1600 - Spams DMA3 and gets buffer overrun
1602 size = DATA_SIZE - cdr.FifoOffset;
1605 if (size > max_words * 4)
1606 size = max_words * 4;
1609 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1610 cdr.FifoOffset += size;
1612 if (size < cdsize) {
1613 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1614 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1616 psxCpu->Clear(madr, cdsize / 4);
1618 CDRDMA_INT((cdsize/4) * 24);
1620 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1622 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1623 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1627 psxRegs.cycle += (cdsize/4) * 24 - 20;
1632 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1636 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1640 void cdrDmaInterrupt(void)
1642 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1644 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1649 static void getCdInfo(void)
1653 CDR_getTN(cdr.ResultTN);
1654 CDR_getTD(0, cdr.SetSectorEnd);
1655 tmp = cdr.SetSectorEnd[0];
1656 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1657 cdr.SetSectorEnd[2] = tmp;
1661 memset(&cdr, 0, sizeof(cdr));
1667 cdr.FifoOffset = DATA_SIZE; // fifo empty
1668 if (CdromId[0] == '\0') {
1669 cdr.DriveState = DRIVESTATE_STOPPED;
1673 cdr.DriveState = DRIVESTATE_STANDBY;
1674 cdr.StatP = STATUS_ROTATING;
1677 // BIOS player - default values
1678 cdr.AttenuatorLeftToLeft = 0x80;
1679 cdr.AttenuatorLeftToRight = 0x00;
1680 cdr.AttenuatorRightToLeft = 0x00;
1681 cdr.AttenuatorRightToRight = 0x80;
1686 int cdrFreeze(void *f, int Mode) {
1690 if (Mode == 0 && !Config.Cdda)
1693 cdr.freeze_ver = 0x63647202;
1694 gzfreeze(&cdr, sizeof(cdr));
1697 cdr.ParamP = cdr.ParamC;
1698 tmp = cdr.FifoOffset;
1701 gzfreeze(&tmp, sizeof(tmp));
1706 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1707 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1708 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1709 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1711 // read right sub data
1712 tmpp[0] = btoi(cdr.Prev[0]);
1713 tmpp[1] = btoi(cdr.Prev[1]);
1714 tmpp[2] = btoi(cdr.Prev[2]);
1719 if (cdr.freeze_ver < 0x63647202)
1720 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1722 Find_CurTrack(cdr.SetSectorPlay);
1724 CDR_play(cdr.SetSectorPlay);
1727 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1728 // old versions did not latch Reg2, have to fixup..
1729 if (cdr.Reg2 == 0) {
1730 SysPrintf("cdrom: fixing up old savestate\n");
1733 // also did not save Attenuator..
1734 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1735 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1737 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1745 void LidInterrupt(void) {
1747 cdrLidSeekInterrupt();