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;
566 static int cdrSeekTime(unsigned char *target)
568 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
569 int pausePenalty, seekTime = abs(diff) * (cdReadTime / 2000);
570 seekTime = MAX_VALUE(seekTime, 20000);
572 // need this stupidly long penalty or else Spyro2 intro desyncs
573 pausePenalty = (s32)(psxRegs.cycle - cdr.LastReadCycles) > cdReadTime * 4 ? cdReadTime * 25 : 0;
574 seekTime += pausePenalty;
576 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
577 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
581 static u32 cdrAlignTimingHack(u32 cycles)
584 * timing hack for T'ai Fu - Wrath of the Tiger:
585 * The game has a bug where it issues some cdc commands from a low priority
586 * vint handler, however there is a higher priority default bios handler
587 * that acks the vint irq and returns, so game's handler is not reached
588 * (see bios irq handler chains at e004 and the game's irq handling func
589 * at 80036810). For the game to work, vint has to arrive after the bios
590 * vint handler rejects some other irq (of which only cd and rcnt2 are
591 * active), but before the game's handler loop reads I_STAT. The time
592 * window for this is quite small (~1k cycles of so). Apparently this
593 * somehow happens naturally on the real hardware.
595 * Note: always enforcing this breaks other games like Crash PAL version
596 * (inputs get dropped because bios handler doesn't see interrupts).
599 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
601 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
602 vint_rel += PSXCLK / 60;
603 while ((s32)(vint_rel - cycles) < 0)
604 vint_rel += PSXCLK / 60;
608 static void cdrUpdateTransferBuf(const u8 *buf);
609 static void cdrReadInterrupt(void);
610 static void cdrPrepCdda(s16 *buf, int samples);
611 static void cdrAttenuate(s16 *buf, int samples, int stereo);
613 static void msfiAdd(u8 *msfi, u32 count)
627 void cdrPlayReadInterrupt(void)
629 cdr.LastReadCycles = psxRegs.cycle;
636 if (!cdr.Play) return;
638 CDR_LOG( "CDDA - %d:%d:%d\n",
639 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
641 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
642 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
644 SetPlaySeekRead(cdr.StatP, 0);
645 cdr.TrackChanged = TRUE;
648 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
651 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
652 cdrPlayInterrupt_Autopause();
654 if (!cdr.Muted && !Config.Cdda) {
655 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
656 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
657 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
661 msfiAdd(cdr.SetSectorPlay, 1);
663 // update for CdlGetlocP/autopause
664 generate_subq(cdr.SetSectorPlay);
666 CDRPLAYREAD_INT(cdReadTime, 0);
669 #define CMD_PART2 0x100
670 #define CMD_WHILE_NOT_READY 0x200
672 void cdrInterrupt(void) {
673 int start_rotating = 0;
675 u32 cycles, seekTime = 0;
676 u32 second_resp_time = 0;
686 CDR_LOG_I("cmd %02x with irqstat %x\n",
687 cdr.CmdInProgress, cdr.Stat);
690 if (cdr.Irq1Pending) {
691 // hand out the "newest" sector, according to nocash
692 cdrUpdateTransferBuf(CDR_getBuffer());
693 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
694 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
695 cdr.CmdInProgress, cdr.Irq1Pending);
697 cdr.Result[0] = cdr.Irq1Pending;
698 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
706 cdr.Result[0] = cdr.StatP;
707 cdr.Stat = Acknowledge;
709 Cmd = cdr.CmdInProgress;
710 cdr.CmdInProgress = 0;
719 switch (cdr.DriveState) {
720 case DRIVESTATE_PREPARE_CD:
722 // Syphon filter 2 expects commands to work shortly after it sees
723 // STATUS_ROTATING, so give up trying to emulate the startup seq
724 cdr.DriveState = DRIVESTATE_STANDBY;
725 cdr.StatP &= ~STATUS_SEEK;
726 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
730 case DRIVESTATE_LID_OPEN:
731 case DRIVESTATE_RESCAN_CD:
732 // no disk or busy with the initial scan, allowed cmds are limited
733 not_ready = CMD_WHILE_NOT_READY;
737 switch (Cmd | not_ready) {
739 case CdlNop + CMD_WHILE_NOT_READY:
740 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
741 cdr.StatP &= ~STATUS_SHELLOPEN;
745 case CdlSetloc + CMD_WHILE_NOT_READY:
746 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
748 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
749 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))
751 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
752 error = ERROR_INVALIDARG;
757 for (i = 0; i < 3; i++)
758 set_loc[i] = btoi(cdr.Param[i]);
759 memcpy(cdr.SetSector, set_loc, 3);
760 cdr.SetSector[3] = 0;
761 cdr.SetlocPending = 1;
770 cdr.FastBackward = 0;
774 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
776 if (ParamC != 0 && cdr.Param[0] != 0) {
777 int track = btoi( cdr.Param[0] );
779 if (track <= cdr.ResultTN[1])
780 cdr.CurTrack = track;
782 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
784 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
785 for (i = 0; i < 3; i++)
786 set_loc[i] = cdr.ResultTD[2 - i];
787 seekTime = cdrSeekTime(set_loc);
788 memcpy(cdr.SetSectorPlay, set_loc, 3);
791 else if (cdr.SetlocPending) {
792 seekTime = cdrSeekTime(cdr.SetSector);
793 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
796 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
797 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
799 cdr.SetlocPending = 0;
802 Rayman: detect track changes
805 Twisted Metal 2: skip PREGAP + starting accurate SubQ
806 - plays tracks without retry play
808 Wild 9: skip PREGAP + starting accurate SubQ
809 - plays tracks without retry play
811 Find_CurTrack(cdr.SetSectorPlay);
812 generate_subq(cdr.SetSectorPlay);
813 cdr.LocL[0] = LOCL_INVALID;
814 cdr.SubqForwardSectors = 1;
815 cdr.TrackChanged = FALSE;
819 CDR_play(cdr.SetSectorPlay);
821 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
823 // BIOS player - set flag again
826 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
831 // TODO: error 80 if stopped
834 // GameShark CD Player: Calls 2x + Play 2x
836 cdr.FastBackward = 0;
842 // GameShark CD Player: Calls 2x + Play 2x
843 cdr.FastBackward = 1;
848 if (cdr.DriveState != DRIVESTATE_STOPPED) {
849 error = ERROR_INVALIDARG;
852 second_resp_time = cdReadTime * 125 / 2;
856 case CdlStandby + CMD_PART2:
862 // grab time for current track
863 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
865 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
866 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
867 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
872 SetPlaySeekRead(cdr.StatP, 0);
873 cdr.StatP &= ~STATUS_ROTATING;
874 cdr.LocL[0] = LOCL_INVALID;
876 second_resp_time = 0x800;
877 if (cdr.DriveState == DRIVESTATE_STANDBY)
878 second_resp_time = cdReadTime * 30 / 2;
880 cdr.DriveState = DRIVESTATE_STOPPED;
883 case CdlStop + CMD_PART2:
891 Gundam Battle Assault 2: much slower (*)
892 - Fixes boot, gameplay
894 Hokuto no Ken 2: slower
895 - Fixes intro + subtitles
897 InuYasha - Feudal Fairy Tale: slower
900 /* Gameblabla - Tightening the timings (as taken from Duckstation).
901 * The timings from Duckstation are based upon hardware tests.
902 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
903 * seems to be timing sensitive as it can depend on the CPU's clock speed.
905 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
907 second_resp_time = 7000;
911 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
913 SetPlaySeekRead(cdr.StatP, 0);
916 case CdlPause + CMD_PART2:
921 case CdlReset + CMD_WHILE_NOT_READY:
924 SetPlaySeekRead(cdr.StatP, 0);
925 cdr.LocL[0] = LOCL_INVALID;
927 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
928 second_resp_time = not_ready ? 70000 : 4100000;
932 case CdlReset + CMD_PART2:
933 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
946 cdr.File = cdr.Param[0];
947 cdr.Channel = cdr.Param[1];
951 case CdlSetmode + CMD_WHILE_NOT_READY:
952 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
953 cdr.Mode = cdr.Param[0];
957 case CdlGetparam + CMD_WHILE_NOT_READY:
958 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
960 cdr.Result[1] = cdr.Mode;
962 cdr.Result[3] = cdr.File;
963 cdr.Result[4] = cdr.Channel;
967 if (cdr.LocL[0] == LOCL_INVALID) {
972 memcpy(cdr.Result, cdr.LocL, 8);
977 memcpy(&cdr.Result, &cdr.subq, 8);
980 case CdlReadT: // SetSession?
982 second_resp_time = cdReadTime * 290 / 4;
986 case CdlReadT + CMD_PART2:
992 if (CDR_getTN(cdr.ResultTN) == -1) {
993 cdr.Stat = DiskError;
994 cdr.Result[0] |= STATUS_ERROR;
996 cdr.Stat = Acknowledge;
997 cdr.Result[1] = itob(cdr.ResultTN[0]);
998 cdr.Result[2] = itob(cdr.ResultTN[1]);
1003 cdr.Track = btoi(cdr.Param[0]);
1005 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1006 cdr.Stat = DiskError;
1007 cdr.Result[0] |= STATUS_ERROR;
1009 cdr.Stat = Acknowledge;
1010 cdr.Result[0] = cdr.StatP;
1011 cdr.Result[1] = itob(cdr.ResultTD[2]);
1012 cdr.Result[2] = itob(cdr.ResultTD[1]);
1013 /* According to Nocash's documentation, the function doesn't care about ff.
1014 * This can be seen also in Mednafen's implementation. */
1015 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1023 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1025 seekTime = cdrSeekTime(cdr.SetSector);
1026 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1028 Crusaders of Might and Magic = 0.5x-4x
1029 - fix cutscene speech start
1031 Eggs of Steel = 2x-?
1035 - fix cutscene speech
1040 second_resp_time = cdReadTime + seekTime;
1044 case CdlSeekL + CMD_PART2:
1045 case CdlSeekP + CMD_PART2:
1046 SetPlaySeekRead(cdr.StatP, 0);
1047 cdr.Result[0] = cdr.StatP;
1048 cdr.Stat = Complete;
1050 Find_CurTrack(cdr.SetSectorPlay);
1051 read_ok = ReadTrack(cdr.SetSectorPlay);
1052 if (read_ok && (buf = CDR_getBuffer()))
1053 memcpy(cdr.LocL, buf, 8);
1054 UpdateSubq(cdr.SetSectorPlay);
1055 cdr.TrackChanged = FALSE;
1059 case CdlTest + CMD_WHILE_NOT_READY:
1060 switch (cdr.Param[0]) {
1061 case 0x20: // System Controller ROM Version
1063 memcpy(cdr.Result, Test20, 4);
1067 memcpy(cdr.Result, Test22, 4);
1069 case 0x23: case 0x24:
1071 memcpy(cdr.Result, Test23, 4);
1077 second_resp_time = 20480;
1080 case CdlID + CMD_PART2:
1082 cdr.Result[0] = cdr.StatP;
1087 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1088 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1089 cdr.Result[1] = 0xc0;
1093 cdr.Result[1] |= 0x10;
1094 if (CdromId[0] == '\0')
1095 cdr.Result[1] |= 0x80;
1097 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1099 /* This adds the string "PCSX" in Playstation bios boot screen */
1100 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1101 cdr.Stat = Complete;
1105 case CdlInit + CMD_WHILE_NOT_READY:
1108 SetPlaySeekRead(cdr.StatP, 0);
1109 // yes, it really sets STATUS_SHELLOPEN
1110 cdr.StatP |= STATUS_SHELLOPEN;
1111 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1117 case CdlGetQ + CMD_WHILE_NOT_READY:
1121 case CdlReadToc + CMD_WHILE_NOT_READY:
1122 cdr.LocL[0] = LOCL_INVALID;
1123 second_resp_time = cdReadTime * 180 / 4;
1127 case CdlReadToc + CMD_PART2:
1128 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1129 cdr.Stat = Complete;
1134 if (cdr.Reading && !cdr.SetlocPending)
1137 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1139 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1140 // Read* acts as play for cdda tracks in cdda mode
1144 if (cdr.SetlocPending) {
1145 seekTime = cdrSeekTime(cdr.SetSector);
1146 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1147 cdr.SetlocPending = 0;
1150 cdr.FirstSector = 1;
1152 // Fighting Force 2 - update subq time immediately
1154 UpdateSubq(cdr.SetSectorPlay);
1155 cdr.LocL[0] = LOCL_INVALID;
1156 cdr.SubqForwardSectors = 1;
1158 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1160 if (Config.hacks.cdr_read_timing)
1161 cycles = cdrAlignTimingHack(cycles);
1162 CDRPLAYREAD_INT(cycles, 1);
1164 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1170 error = ERROR_INVALIDCMD;
1174 CDR_LOG_I("cmd %02x error %02x\n", Cmd, error);
1176 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1177 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1178 cdr.Stat = DiskError;
1182 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1183 cdr.DriveState = DRIVESTATE_STANDBY;
1184 cdr.StatP |= STATUS_ROTATING;
1187 if (second_resp_time) {
1188 cdr.CmdInProgress = Cmd | 0x100;
1189 CDR_INT(second_resp_time);
1191 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1192 cdr.CmdInProgress = cdr.Cmd;
1193 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1200 #define ssat32_to_16(v) \
1201 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1203 #define ssat32_to_16(v) do { \
1204 if (v < -32768) v = -32768; \
1205 else if (v > 32767) v = 32767; \
1209 static void cdrPrepCdda(s16 *buf, int samples)
1211 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1213 for (i = 0; i < samples; i++) {
1214 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1215 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1220 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1223 int ll = cdr.AttenuatorLeftToLeft;
1224 int lr = cdr.AttenuatorLeftToRight;
1225 int rl = cdr.AttenuatorRightToLeft;
1226 int rr = cdr.AttenuatorRightToRight;
1228 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1231 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1235 for (i = 0; i < samples; i++) {
1238 l = (l * ll + r * rl) >> 7;
1239 r = (r * rr + l * lr) >> 7;
1247 for (i = 0; i < samples; i++) {
1249 l = l * (ll + rl) >> 7;
1250 //r = r * (rr + lr) >> 7;
1258 static void cdrReadInterruptSetResult(unsigned char result)
1261 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1262 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1263 cdr.CmdInProgress, cdr.Stat);
1264 cdr.Irq1Pending = result;
1268 cdr.Result[0] = result;
1269 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1273 static void cdrUpdateTransferBuf(const u8 *buf)
1277 memcpy(cdr.Transfer, buf, DATA_SIZE);
1278 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1279 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1280 if (cdr.FifoOffset < 2048 + 12)
1281 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1284 static void cdrReadInterrupt(void)
1286 u8 *buf = NULL, *hdr;
1290 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1291 msfiAdd(subqPos, cdr.SubqForwardSectors);
1292 UpdateSubq(subqPos);
1293 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1294 cdr.SubqForwardSectors++;
1295 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1299 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1300 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1302 read_ok = ReadTrack(cdr.SetSectorPlay);
1304 buf = CDR_getBuffer();
1309 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1310 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1313 memcpy(cdr.LocL, buf, 8);
1315 if (!cdr.Stat && !cdr.Irq1Pending)
1316 cdrUpdateTransferBuf(buf);
1318 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1320 // Firemen 2: Multi-XA files - briefings, cutscenes
1321 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1323 cdr.Channel = hdr[1];
1327 * Skips playing on channel 255.
1328 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1329 * TODO : Check if this is the proper behaviour.
1331 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1332 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1334 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1335 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1336 cdr.FirstSector = 0;
1338 else cdr.FirstSector = -1;
1343 Croc 2: $40 - only FORM1 (*)
1344 Judge Dredd: $C8 - only FORM1 (*)
1345 Sim Theme Park - no adpcm at all (zero)
1348 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1349 cdrReadInterruptSetResult(cdr.StatP);
1351 msfiAdd(cdr.SetSectorPlay, 1);
1353 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1362 bit 5 - 1 result ready
1364 bit 7 - 1 command being processed
1367 unsigned char cdrRead0(void) {
1368 if (cdr.ResultReady)
1373 cdr.Ctrl |= 0x40; // data fifo not empty
1375 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1378 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1380 return psxHu8(0x1800) = cdr.Ctrl;
1383 void cdrWrite0(unsigned char rt) {
1384 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1386 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1389 unsigned char cdrRead1(void) {
1390 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1391 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1395 if (cdr.ResultP == cdr.ResultC)
1396 cdr.ResultReady = 0;
1398 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1400 return psxHu8(0x1801);
1403 void cdrWrite1(unsigned char rt) {
1404 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1405 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1407 switch (cdr.Ctrl & 3) {
1411 cdr.AttenuatorRightToRightT = rt;
1417 #ifdef CDR_LOG_CMD_IRQ
1418 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1421 SysPrintf(" Param[%d] = {", cdr.ParamC);
1422 for (i = 0; i < cdr.ParamC; i++)
1423 SysPrintf(" %x,", cdr.Param[i]);
1430 cdr.ResultReady = 0;
1433 if (!cdr.CmdInProgress) {
1434 cdr.CmdInProgress = rt;
1435 // should be something like 12k + controller delays
1439 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1440 rt, cdr.Cmd, cdr.CmdInProgress);
1441 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1442 cdr.CmdInProgress = rt;
1448 unsigned char cdrRead2(void) {
1449 unsigned char ret = cdr.Transfer[0x920];
1451 if (cdr.FifoOffset < cdr.FifoSize)
1452 ret = cdr.Transfer[cdr.FifoOffset++];
1454 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1456 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1460 void cdrWrite2(unsigned char rt) {
1461 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1462 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1464 switch (cdr.Ctrl & 3) {
1466 if (cdr.ParamC < 8) // FIXME: size and wrapping
1467 cdr.Param[cdr.ParamC++] = rt;
1474 cdr.AttenuatorLeftToLeftT = rt;
1477 cdr.AttenuatorRightToLeftT = rt;
1482 unsigned char cdrRead3(void) {
1484 psxHu8(0x1803) = cdr.Stat | 0xE0;
1486 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1488 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1489 return psxHu8(0x1803);
1492 void cdrWrite3(unsigned char rt) {
1493 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1494 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1496 switch (cdr.Ctrl & 3) {
1500 if (cdr.Stat & rt) {
1501 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1502 + psxRegs.intCycle[PSXINT_CDR].cycle;
1503 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1504 #ifdef CDR_LOG_CMD_IRQ
1505 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1506 !!pending, cdr.CmdInProgress,
1507 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1509 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1510 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1513 if (cdr.CmdInProgress) {
1514 c = 2048 - (psxRegs.cycle - nextCycle);
1515 c = MAX_VALUE(c, 512);
1526 cdr.AttenuatorLeftToRightT = rt;
1530 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1531 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1532 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1533 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1539 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1540 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1542 else if (rt & 0x80) {
1543 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1544 case MODE_SIZE_2328:
1546 cdr.FifoOffset = 12;
1547 cdr.FifoSize = 2048 + 12;
1550 case MODE_SIZE_2340:
1553 cdr.FifoSize = 2340;
1557 else if (!(rt & 0xc0))
1558 cdr.FifoOffset = DATA_SIZE; // fifo empty
1561 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1562 u32 cdsize, max_words;
1567 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1568 if (cdr.FifoOffset == 0) {
1570 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1575 switch (chcr & 0x71000000) {
1577 ptr = getDmaRam(madr, &max_words);
1578 if (ptr == INVALID_PTR) {
1579 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1583 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1586 GS CDX: Enhancement CD crash
1589 - Spams DMA3 and gets buffer overrun
1591 size = DATA_SIZE - cdr.FifoOffset;
1594 if (size > max_words * 4)
1595 size = max_words * 4;
1598 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1599 cdr.FifoOffset += size;
1601 if (size < cdsize) {
1602 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1603 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1605 psxCpu->Clear(madr, cdsize / 4);
1607 CDRDMA_INT((cdsize/4) * 24);
1609 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1611 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1612 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1616 psxRegs.cycle += (cdsize/4) * 24 - 20;
1621 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1625 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1629 void cdrDmaInterrupt(void)
1631 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1633 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1638 static void getCdInfo(void)
1642 CDR_getTN(cdr.ResultTN);
1643 CDR_getTD(0, cdr.SetSectorEnd);
1644 tmp = cdr.SetSectorEnd[0];
1645 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1646 cdr.SetSectorEnd[2] = tmp;
1650 memset(&cdr, 0, sizeof(cdr));
1656 cdr.FifoOffset = DATA_SIZE; // fifo empty
1657 if (CdromId[0] == '\0') {
1658 cdr.DriveState = DRIVESTATE_STOPPED;
1662 cdr.DriveState = DRIVESTATE_STANDBY;
1663 cdr.StatP = STATUS_ROTATING;
1666 // BIOS player - default values
1667 cdr.AttenuatorLeftToLeft = 0x80;
1668 cdr.AttenuatorLeftToRight = 0x00;
1669 cdr.AttenuatorRightToLeft = 0x00;
1670 cdr.AttenuatorRightToRight = 0x80;
1675 int cdrFreeze(void *f, int Mode) {
1679 if (Mode == 0 && !Config.Cdda)
1682 cdr.freeze_ver = 0x63647202;
1683 gzfreeze(&cdr, sizeof(cdr));
1686 cdr.ParamP = cdr.ParamC;
1687 tmp = cdr.FifoOffset;
1690 gzfreeze(&tmp, sizeof(tmp));
1695 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1696 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1697 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1698 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1700 // read right sub data
1701 tmpp[0] = btoi(cdr.Prev[0]);
1702 tmpp[1] = btoi(cdr.Prev[1]);
1703 tmpp[2] = btoi(cdr.Prev[2]);
1708 if (cdr.freeze_ver < 0x63647202)
1709 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1711 Find_CurTrack(cdr.SetSectorPlay);
1713 CDR_play(cdr.SetSectorPlay);
1716 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1717 // old versions did not latch Reg2, have to fixup..
1718 if (cdr.Reg2 == 0) {
1719 SysPrintf("cdrom: fixing up old savestate\n");
1722 // also did not save Attenuator..
1723 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1724 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1726 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1734 void LidInterrupt(void) {
1736 cdrLidSeekInterrupt();