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 ReportDelay;
69 unsigned char unused3[2];
70 unsigned int freeze_ver;
72 unsigned char Prev[4];
73 unsigned char Param[8];
74 unsigned char Result[16];
78 unsigned char ResultC;
79 unsigned char ResultP;
80 unsigned char ResultReady;
82 unsigned char SubqForwardSectors;
83 unsigned char SetlocPending;
86 unsigned char ResultTN[6];
87 unsigned char ResultTD[4];
88 unsigned char SetSectorPlay[4];
89 unsigned char SetSectorEnd[4];
90 unsigned char SetSector[4];
94 int Mode, File, Channel;
95 unsigned char LocL[8];
115 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
116 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
117 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
118 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
120 static s16 read_buf[CD_FRAMESIZE_RAW/2];
122 /* CD-ROM magic numbers */
123 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
128 #define CdlBackward 5
136 #define CdlSetfilter 13
137 #define CdlSetmode 14
138 #define CdlGetparam 15
139 #define CdlGetlocL 16
140 #define CdlGetlocP 17
146 #define CdlSetclock 23
147 #define CdlGetclock 24
153 #define CdlReadToc 30
155 #ifdef CDR_LOG_CMD_IRQ
156 static const char * const CmdName[0x100] = {
157 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
158 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
159 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
160 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
161 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
162 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
163 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
164 "CdlInit", NULL, "CDlReadToc", NULL
168 unsigned char Test04[] = { 0 };
169 unsigned char Test05[] = { 0 };
170 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
171 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
172 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
178 #define Acknowledge 3
183 #define MODE_SPEED (1<<7) // 0x80
184 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
185 #define MODE_SIZE_2340 (1<<5) // 0x20
186 #define MODE_SIZE_2328 (1<<4) // 0x10
187 #define MODE_SIZE_2048 (0<<4) // 0x00
188 #define MODE_SF (1<<3) // 0x08 channel on/off
189 #define MODE_REPORT (1<<2) // 0x04
190 #define MODE_AUTOPAUSE (1<<1) // 0x02
191 #define MODE_CDDA (1<<0) // 0x01
194 #define STATUS_PLAY (1<<7) // 0x80
195 #define STATUS_SEEK (1<<6) // 0x40
196 #define STATUS_READ (1<<5) // 0x20
197 #define STATUS_SHELLOPEN (1<<4) // 0x10
198 #define STATUS_UNKNOWN3 (1<<3) // 0x08
199 #define STATUS_UNKNOWN2 (1<<2) // 0x04
200 #define STATUS_ROTATING (1<<1) // 0x02
201 #define STATUS_ERROR (1<<0) // 0x01
204 #define ERROR_NOTREADY (1<<7) // 0x80
205 #define ERROR_INVALIDCMD (1<<6) // 0x40
206 #define ERROR_INVALIDARG (1<<5) // 0x20
208 // 1x = 75 sectors per second
209 // PSXCLK = 1 sec in the ps
210 // so (PSXCLK / 75) = cdr read time (linuzappz)
211 #define cdReadTime (PSXCLK / 75)
213 #define LOCL_INVALID 0xff
214 #define SUBQ_FORWARD_SECTORS 2u
217 DRIVESTATE_STANDBY = 0, // pause, play, read
219 DRIVESTATE_RESCAN_CD,
220 DRIVESTATE_PREPARE_CD,
224 static struct CdrStat stat;
226 static unsigned int msf2sec(const u8 *msf) {
227 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
230 // for that weird psemu API..
231 static unsigned int fsm2sec(const u8 *msf) {
232 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
235 static void sec2msf(unsigned int s, u8 *msf) {
236 msf[0] = s / 75 / 60;
237 s = s - msf[0] * 75 * 60;
244 #define CDR_INT(eCycle) { \
245 psxRegs.interrupt |= (1 << PSXINT_CDR); \
246 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
247 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
248 new_dyna_set_event(PSXINT_CDR, eCycle); \
251 // cdrPlayReadInterrupt
252 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
254 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
256 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
258 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
259 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
260 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
263 // cdrLidSeekInterrupt
264 #define CDRLID_INT(eCycle) { \
265 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
266 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
267 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
268 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
271 #define StopReading() { \
273 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
276 #define StopCdda() { \
277 if (cdr.Play && !Config.Cdda) CDR_stop(); \
279 cdr.FastForward = 0; \
280 cdr.FastBackward = 0; \
283 #define SetPlaySeekRead(x, f) { \
284 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
288 #define SetResultSize(size) { \
290 cdr.ResultC = size; \
291 cdr.ResultReady = 1; \
294 static void setIrq(int log_cmd)
296 if (cdr.Stat & cdr.Reg2)
297 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
299 #ifdef CDR_LOG_CMD_IRQ
303 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
304 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
305 for (i = 0; i < cdr.ResultC; i++)
306 SysPrintf("%02x ", cdr.Result[i]);
312 // timing used in this function was taken from tests on real hardware
313 // (yes it's slow, but you probably don't want to modify it)
314 void cdrLidSeekInterrupt(void)
316 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
318 switch (cdr.DriveState) {
320 case DRIVESTATE_STANDBY:
323 SetPlaySeekRead(cdr.StatP, 0);
325 if (CDR_getStatus(&stat) == -1)
328 if (stat.Status & STATUS_SHELLOPEN)
330 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
331 cdr.DriveState = DRIVESTATE_LID_OPEN;
336 case DRIVESTATE_LID_OPEN:
337 if (CDR_getStatus(&stat) == -1)
338 stat.Status &= ~STATUS_SHELLOPEN;
341 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
342 cdr.StatP |= STATUS_SHELLOPEN;
344 // could generate error irq here, but real hardware
345 // only sometimes does that
346 // (not done when lots of commands are sent?)
348 CDRLID_INT(cdReadTime * 30);
351 else if (cdr.StatP & STATUS_ROTATING) {
352 cdr.StatP &= ~STATUS_ROTATING;
354 else if (!(stat.Status & STATUS_SHELLOPEN)) {
358 // cdr.StatP STATUS_SHELLOPEN is "sticky"
359 // and is only cleared by CdlNop
361 cdr.DriveState = DRIVESTATE_RESCAN_CD;
362 CDRLID_INT(cdReadTime * 105);
367 CDRLID_INT(cdReadTime * 3);
370 case DRIVESTATE_RESCAN_CD:
371 cdr.StatP |= STATUS_ROTATING;
372 cdr.DriveState = DRIVESTATE_PREPARE_CD;
374 // this is very long on real hardware, over 6 seconds
375 // make it a bit faster here...
376 CDRLID_INT(cdReadTime * 150);
379 case DRIVESTATE_PREPARE_CD:
380 if (cdr.StatP & STATUS_SEEK) {
381 SetPlaySeekRead(cdr.StatP, 0);
382 cdr.DriveState = DRIVESTATE_STANDBY;
385 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
386 CDRLID_INT(cdReadTime * 26);
392 static void Find_CurTrack(const u8 *time)
396 current = msf2sec(time);
398 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
399 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
400 sect = fsm2sec(cdr.ResultTD);
401 if (sect - current >= 150)
406 static void generate_subq(const u8 *time)
408 unsigned char start[3], next[3];
409 unsigned int this_s, start_s, next_s, pregap;
412 CDR_getTD(cdr.CurTrack, start);
413 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
415 CDR_getTD(cdr.CurTrack + 1, next);
418 // last track - cd size
420 next[0] = cdr.SetSectorEnd[2];
421 next[1] = cdr.SetSectorEnd[1];
422 next[2] = cdr.SetSectorEnd[0];
425 this_s = msf2sec(time);
426 start_s = fsm2sec(start);
427 next_s = fsm2sec(next);
429 cdr.TrackChanged = FALSE;
431 if (next_s - this_s < pregap) {
432 cdr.TrackChanged = TRUE;
439 relative_s = this_s - start_s;
440 if (relative_s < 0) {
442 relative_s = -relative_s;
444 sec2msf(relative_s, cdr.subq.Relative);
446 cdr.subq.Track = itob(cdr.CurTrack);
447 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
448 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
449 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
450 cdr.subq.Absolute[0] = itob(time[0]);
451 cdr.subq.Absolute[1] = itob(time[1]);
452 cdr.subq.Absolute[2] = itob(time[2]);
455 static int ReadTrack(const u8 *time)
457 unsigned char tmp[3];
460 tmp[0] = itob(time[0]);
461 tmp[1] = itob(time[1]);
462 tmp[2] = itob(time[2]);
464 if (memcmp(cdr.Prev, tmp, 3) == 0)
467 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
469 read_ok = CDR_readTrack(tmp);
471 memcpy(cdr.Prev, tmp, 3);
475 static void UpdateSubq(const u8 *time)
477 const struct SubQ *subq;
483 subq = (struct SubQ *)CDR_getBufferSub(MSF2SECT(time[0], time[1], time[2]));
484 if (subq != NULL && cdr.CurTrack == 1) {
485 crc = calcCrc((u8 *)subq + 12, 10);
486 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
487 cdr.subq.Track = subq->TrackNumber;
488 cdr.subq.Index = subq->IndexNumber;
489 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
490 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
493 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
494 time[0], time[1], time[2]);
501 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
502 cdr.subq.Track, cdr.subq.Index,
503 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
504 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
507 static void cdrPlayInterrupt_Autopause()
510 boolean abs_lev_chselect;
513 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
514 CDR_LOG( "CDDA STOP\n" );
516 // Magic the Gathering
517 // - looping territory cdda
520 //cdr.ResultReady = 1;
521 //cdr.Stat = DataReady;
523 setIrq(0x1000); // 0x1000 just for logging purposes
526 SetPlaySeekRead(cdr.StatP, 0);
528 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
529 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
531 cdr.Result[0] = cdr.StatP;
532 cdr.Result[1] = cdr.subq.Track;
533 cdr.Result[2] = cdr.subq.Index;
535 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
537 /* 8 is a hack. For accuracy, it should be 588. */
538 for (i = 0; i < 8; i++)
540 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
542 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
543 abs_lev_max |= abs_lev_chselect << 15;
545 if (cdr.subq.Absolute[2] & 0x10) {
546 cdr.Result[3] = cdr.subq.Relative[0];
547 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
548 cdr.Result[5] = cdr.subq.Relative[2];
551 cdr.Result[3] = cdr.subq.Absolute[0];
552 cdr.Result[4] = cdr.subq.Absolute[1];
553 cdr.Result[5] = cdr.subq.Absolute[2];
556 cdr.Result[6] = abs_lev_max >> 0;
557 cdr.Result[7] = abs_lev_max >> 8;
559 // Rayman: Logo freeze (resultready + dataready)
561 cdr.Stat = DataReady;
572 static int cdrSeekTime(unsigned char *target)
574 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
575 int pausePenalty, seekTime = abs(diff) * (cdReadTime / 2000);
576 seekTime = MAX_VALUE(seekTime, 20000);
578 // need this stupidly long penalty or else Spyro2 intro desyncs
579 pausePenalty = (s32)(psxRegs.cycle - cdr.LastReadCycles) > cdReadTime * 4 ? cdReadTime * 25 : 0;
580 seekTime += pausePenalty;
582 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
583 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
587 static u32 cdrAlignTimingHack(u32 cycles)
590 * timing hack for T'ai Fu - Wrath of the Tiger:
591 * The game has a bug where it issues some cdc commands from a low priority
592 * vint handler, however there is a higher priority default bios handler
593 * that acks the vint irq and returns, so game's handler is not reached
594 * (see bios irq handler chains at e004 and the game's irq handling func
595 * at 80036810). For the game to work, vint has to arrive after the bios
596 * vint handler rejects some other irq (of which only cd and rcnt2 are
597 * active), but before the game's handler loop reads I_STAT. The time
598 * window for this is quite small (~1k cycles of so). Apparently this
599 * somehow happens naturally on the real hardware.
601 * Note: always enforcing this breaks other games like Crash PAL version
602 * (inputs get dropped because bios handler doesn't see interrupts).
605 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
607 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
608 vint_rel += PSXCLK / 60;
609 while ((s32)(vint_rel - cycles) < 0)
610 vint_rel += PSXCLK / 60;
614 static void cdrUpdateTransferBuf(const u8 *buf);
615 static void cdrReadInterrupt(void);
616 static void cdrPrepCdda(s16 *buf, int samples);
617 static void cdrAttenuate(s16 *buf, int samples, int stereo);
619 static void msfiAdd(u8 *msfi, u32 count)
633 void cdrPlayReadInterrupt(void)
635 cdr.LastReadCycles = psxRegs.cycle;
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 if (++cdr.errorRetryhack > 100)
760 error = ERROR_INVALIDARG;
765 for (i = 0; i < 3; i++)
766 set_loc[i] = btoi(cdr.Param[i]);
767 memcpy(cdr.SetSector, set_loc, 3);
768 cdr.SetSector[3] = 0;
769 cdr.SetlocPending = 1;
770 cdr.errorRetryhack = 0;
779 cdr.FastBackward = 0;
783 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
785 if (ParamC != 0 && cdr.Param[0] != 0) {
786 int track = btoi( cdr.Param[0] );
788 if (track <= cdr.ResultTN[1])
789 cdr.CurTrack = track;
791 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
793 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
794 for (i = 0; i < 3; i++)
795 set_loc[i] = cdr.ResultTD[2 - i];
796 seekTime = cdrSeekTime(set_loc);
797 memcpy(cdr.SetSectorPlay, set_loc, 3);
800 else if (cdr.SetlocPending) {
801 seekTime = cdrSeekTime(cdr.SetSector);
802 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
805 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
806 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
808 cdr.SetlocPending = 0;
811 Rayman: detect track changes
814 Twisted Metal 2: skip PREGAP + starting accurate SubQ
815 - plays tracks without retry play
817 Wild 9: skip PREGAP + starting accurate SubQ
818 - plays tracks without retry play
820 Find_CurTrack(cdr.SetSectorPlay);
821 generate_subq(cdr.SetSectorPlay);
822 cdr.LocL[0] = LOCL_INVALID;
823 cdr.SubqForwardSectors = 1;
824 cdr.TrackChanged = FALSE;
826 cdr.ReportDelay = 60;
829 CDR_play(cdr.SetSectorPlay);
831 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
833 // BIOS player - set flag again
836 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
841 // TODO: error 80 if stopped
844 // GameShark CD Player: Calls 2x + Play 2x
846 cdr.FastBackward = 0;
852 // GameShark CD Player: Calls 2x + Play 2x
853 cdr.FastBackward = 1;
858 if (cdr.DriveState != DRIVESTATE_STOPPED) {
859 error = ERROR_INVALIDARG;
862 second_resp_time = cdReadTime * 125 / 2;
866 case CdlStandby + CMD_PART2:
872 // grab time for current track
873 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
875 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
876 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
877 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
882 SetPlaySeekRead(cdr.StatP, 0);
883 cdr.StatP &= ~STATUS_ROTATING;
884 cdr.LocL[0] = LOCL_INVALID;
886 second_resp_time = 0x800;
887 if (cdr.DriveState == DRIVESTATE_STANDBY)
888 second_resp_time = cdReadTime * 30 / 2;
890 cdr.DriveState = DRIVESTATE_STOPPED;
893 case CdlStop + CMD_PART2:
901 Gundam Battle Assault 2: much slower (*)
902 - Fixes boot, gameplay
904 Hokuto no Ken 2: slower
905 - Fixes intro + subtitles
907 InuYasha - Feudal Fairy Tale: slower
910 /* Gameblabla - Tightening the timings (as taken from Duckstation).
911 * The timings from Duckstation are based upon hardware tests.
912 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
913 * seems to be timing sensitive as it can depend on the CPU's clock speed.
915 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
917 second_resp_time = 7000;
921 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
923 SetPlaySeekRead(cdr.StatP, 0);
926 case CdlPause + CMD_PART2:
931 case CdlReset + CMD_WHILE_NOT_READY:
934 SetPlaySeekRead(cdr.StatP, 0);
935 cdr.LocL[0] = LOCL_INVALID;
937 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
938 second_resp_time = not_ready ? 70000 : 4100000;
942 case CdlReset + CMD_PART2:
943 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
956 cdr.File = cdr.Param[0];
957 cdr.Channel = cdr.Param[1];
961 case CdlSetmode + CMD_WHILE_NOT_READY:
962 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
963 cdr.Mode = cdr.Param[0];
967 case CdlGetparam + CMD_WHILE_NOT_READY:
968 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
970 cdr.Result[1] = cdr.Mode;
972 cdr.Result[3] = cdr.File;
973 cdr.Result[4] = cdr.Channel;
977 if (cdr.LocL[0] == LOCL_INVALID) {
982 memcpy(cdr.Result, cdr.LocL, 8);
987 memcpy(&cdr.Result, &cdr.subq, 8);
990 case CdlReadT: // SetSession?
992 second_resp_time = cdReadTime * 290 / 4;
996 case CdlReadT + CMD_PART2:
1002 if (CDR_getTN(cdr.ResultTN) == -1) {
1003 cdr.Stat = DiskError;
1004 cdr.Result[0] |= STATUS_ERROR;
1006 cdr.Stat = Acknowledge;
1007 cdr.Result[1] = itob(cdr.ResultTN[0]);
1008 cdr.Result[2] = itob(cdr.ResultTN[1]);
1013 cdr.Track = btoi(cdr.Param[0]);
1015 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1016 cdr.Stat = DiskError;
1017 cdr.Result[0] |= STATUS_ERROR;
1019 cdr.Stat = Acknowledge;
1020 cdr.Result[0] = cdr.StatP;
1021 cdr.Result[1] = itob(cdr.ResultTD[2]);
1022 cdr.Result[2] = itob(cdr.ResultTD[1]);
1023 /* According to Nocash's documentation, the function doesn't care about ff.
1024 * This can be seen also in Mednafen's implementation. */
1025 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1033 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1035 seekTime = cdrSeekTime(cdr.SetSector);
1036 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1038 Crusaders of Might and Magic = 0.5x-4x
1039 - fix cutscene speech start
1041 Eggs of Steel = 2x-?
1045 - fix cutscene speech
1050 second_resp_time = cdReadTime + seekTime;
1054 case CdlSeekL + CMD_PART2:
1055 case CdlSeekP + CMD_PART2:
1056 SetPlaySeekRead(cdr.StatP, 0);
1057 cdr.Result[0] = cdr.StatP;
1058 cdr.Stat = Complete;
1060 Find_CurTrack(cdr.SetSectorPlay);
1061 read_ok = ReadTrack(cdr.SetSectorPlay);
1062 if (read_ok && (buf = CDR_getBuffer()))
1063 memcpy(cdr.LocL, buf, 8);
1064 UpdateSubq(cdr.SetSectorPlay);
1065 cdr.TrackChanged = FALSE;
1069 case CdlTest + CMD_WHILE_NOT_READY:
1070 switch (cdr.Param[0]) {
1071 case 0x20: // System Controller ROM Version
1073 memcpy(cdr.Result, Test20, 4);
1077 memcpy(cdr.Result, Test22, 4);
1079 case 0x23: case 0x24:
1081 memcpy(cdr.Result, Test23, 4);
1087 second_resp_time = 20480;
1090 case CdlID + CMD_PART2:
1092 cdr.Result[0] = cdr.StatP;
1097 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1098 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1099 cdr.Result[1] = 0xc0;
1103 cdr.Result[1] |= 0x10;
1104 if (CdromId[0] == '\0')
1105 cdr.Result[1] |= 0x80;
1107 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1109 /* This adds the string "PCSX" in Playstation bios boot screen */
1110 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1111 cdr.Stat = Complete;
1115 case CdlInit + CMD_WHILE_NOT_READY:
1118 SetPlaySeekRead(cdr.StatP, 0);
1119 // yes, it really sets STATUS_SHELLOPEN
1120 cdr.StatP |= STATUS_SHELLOPEN;
1121 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1127 case CdlGetQ + CMD_WHILE_NOT_READY:
1131 case CdlReadToc + CMD_WHILE_NOT_READY:
1132 cdr.LocL[0] = LOCL_INVALID;
1133 second_resp_time = cdReadTime * 180 / 4;
1137 case CdlReadToc + CMD_PART2:
1138 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1139 cdr.Stat = Complete;
1144 if (cdr.Reading && !cdr.SetlocPending)
1147 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1149 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1150 // Read* acts as play for cdda tracks in cdda mode
1154 if (cdr.SetlocPending) {
1155 seekTime = cdrSeekTime(cdr.SetSector);
1156 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1157 cdr.SetlocPending = 0;
1160 cdr.FirstSector = 1;
1162 // Fighting Force 2 - update subq time immediately
1164 UpdateSubq(cdr.SetSectorPlay);
1165 cdr.LocL[0] = LOCL_INVALID;
1166 cdr.SubqForwardSectors = 1;
1168 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1170 if (Config.hacks.cdr_read_timing)
1171 cycles = cdrAlignTimingHack(cycles);
1172 CDRPLAYREAD_INT(cycles, 1);
1174 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1180 error = ERROR_INVALIDCMD;
1184 CDR_LOG_I("cmd %02x error %02x\n", Cmd, error);
1186 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1187 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1188 cdr.Stat = DiskError;
1192 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1193 cdr.DriveState = DRIVESTATE_STANDBY;
1194 cdr.StatP |= STATUS_ROTATING;
1197 if (second_resp_time) {
1198 cdr.CmdInProgress = Cmd | 0x100;
1199 CDR_INT(second_resp_time);
1201 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1202 cdr.CmdInProgress = cdr.Cmd;
1203 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1210 #define ssat32_to_16(v) \
1211 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1213 #define ssat32_to_16(v) do { \
1214 if (v < -32768) v = -32768; \
1215 else if (v > 32767) v = 32767; \
1219 static void cdrPrepCdda(s16 *buf, int samples)
1221 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1223 for (i = 0; i < samples; i++) {
1224 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1225 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1230 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1233 int ll = cdr.AttenuatorLeftToLeft;
1234 int lr = cdr.AttenuatorLeftToRight;
1235 int rl = cdr.AttenuatorRightToLeft;
1236 int rr = cdr.AttenuatorRightToRight;
1238 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1241 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1245 for (i = 0; i < samples; i++) {
1248 l = (l * ll + r * rl) >> 7;
1249 r = (r * rr + l * lr) >> 7;
1257 for (i = 0; i < samples; i++) {
1259 l = l * (ll + rl) >> 7;
1260 //r = r * (rr + lr) >> 7;
1268 static void cdrReadInterruptSetResult(unsigned char result)
1271 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1272 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1273 cdr.CmdInProgress, cdr.Stat);
1274 cdr.Irq1Pending = result;
1278 cdr.Result[0] = result;
1279 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1283 static void cdrUpdateTransferBuf(const u8 *buf)
1287 memcpy(cdr.Transfer, buf, DATA_SIZE);
1288 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1289 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1290 if (cdr.FifoOffset < 2048 + 12)
1291 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1294 static void cdrReadInterrupt(void)
1296 u8 *buf = NULL, *hdr;
1300 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1301 msfiAdd(subqPos, cdr.SubqForwardSectors);
1302 UpdateSubq(subqPos);
1303 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1304 cdr.SubqForwardSectors++;
1305 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1309 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1310 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1312 read_ok = ReadTrack(cdr.SetSectorPlay);
1314 buf = CDR_getBuffer();
1319 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1320 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1323 memcpy(cdr.LocL, buf, 8);
1325 if (!cdr.Stat && !cdr.Irq1Pending)
1326 cdrUpdateTransferBuf(buf);
1328 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1330 // Firemen 2: Multi-XA files - briefings, cutscenes
1331 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1333 cdr.Channel = hdr[1];
1337 * Skips playing on channel 255.
1338 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1339 * TODO : Check if this is the proper behaviour.
1341 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1342 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1344 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1345 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1346 cdr.FirstSector = 0;
1348 else cdr.FirstSector = -1;
1353 Croc 2: $40 - only FORM1 (*)
1354 Judge Dredd: $C8 - only FORM1 (*)
1355 Sim Theme Park - no adpcm at all (zero)
1358 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1359 cdrReadInterruptSetResult(cdr.StatP);
1361 msfiAdd(cdr.SetSectorPlay, 1);
1363 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1372 bit 5 - 1 result ready
1374 bit 7 - 1 command being processed
1377 unsigned char cdrRead0(void) {
1378 if (cdr.ResultReady)
1383 cdr.Ctrl |= 0x40; // data fifo not empty
1385 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1388 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1390 return psxHu8(0x1800) = cdr.Ctrl;
1393 void cdrWrite0(unsigned char rt) {
1394 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1396 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1399 unsigned char cdrRead1(void) {
1400 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1401 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1405 if (cdr.ResultP == cdr.ResultC)
1406 cdr.ResultReady = 0;
1408 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1410 return psxHu8(0x1801);
1413 void cdrWrite1(unsigned char rt) {
1414 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1415 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1417 switch (cdr.Ctrl & 3) {
1421 cdr.AttenuatorRightToRightT = rt;
1427 #ifdef CDR_LOG_CMD_IRQ
1428 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1431 SysPrintf(" Param[%d] = {", cdr.ParamC);
1432 for (i = 0; i < cdr.ParamC; i++)
1433 SysPrintf(" %x,", cdr.Param[i]);
1440 cdr.ResultReady = 0;
1443 if (!cdr.CmdInProgress) {
1444 cdr.CmdInProgress = rt;
1445 // should be something like 12k + controller delays
1449 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1450 rt, cdr.Cmd, cdr.CmdInProgress);
1451 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1452 cdr.CmdInProgress = rt;
1458 unsigned char cdrRead2(void) {
1459 unsigned char ret = cdr.Transfer[0x920];
1461 if (cdr.FifoOffset < cdr.FifoSize)
1462 ret = cdr.Transfer[cdr.FifoOffset++];
1464 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1466 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1470 void cdrWrite2(unsigned char rt) {
1471 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1472 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1474 switch (cdr.Ctrl & 3) {
1476 if (cdr.ParamC < 8) // FIXME: size and wrapping
1477 cdr.Param[cdr.ParamC++] = rt;
1484 cdr.AttenuatorLeftToLeftT = rt;
1487 cdr.AttenuatorRightToLeftT = rt;
1492 unsigned char cdrRead3(void) {
1494 psxHu8(0x1803) = cdr.Stat | 0xE0;
1496 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1498 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1499 return psxHu8(0x1803);
1502 void cdrWrite3(unsigned char rt) {
1503 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1504 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1506 switch (cdr.Ctrl & 3) {
1510 if (cdr.Stat & rt) {
1511 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1512 + psxRegs.intCycle[PSXINT_CDR].cycle;
1513 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1514 #ifdef CDR_LOG_CMD_IRQ
1515 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1516 !!pending, cdr.CmdInProgress,
1517 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1519 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1520 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1523 if (cdr.CmdInProgress) {
1524 c = 2048 - (psxRegs.cycle - nextCycle);
1525 c = MAX_VALUE(c, 512);
1536 cdr.AttenuatorLeftToRightT = rt;
1540 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1541 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1542 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1543 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1549 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1550 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1552 else if (rt & 0x80) {
1553 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1554 case MODE_SIZE_2328:
1556 cdr.FifoOffset = 12;
1557 cdr.FifoSize = 2048 + 12;
1560 case MODE_SIZE_2340:
1563 cdr.FifoSize = 2340;
1567 else if (!(rt & 0xc0))
1568 cdr.FifoOffset = DATA_SIZE; // fifo empty
1571 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1572 u32 cdsize, max_words;
1577 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1578 if (cdr.FifoOffset == 0) {
1580 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1585 switch (chcr & 0x71000000) {
1587 ptr = getDmaRam(madr, &max_words);
1588 if (ptr == INVALID_PTR) {
1589 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1593 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1596 GS CDX: Enhancement CD crash
1599 - Spams DMA3 and gets buffer overrun
1601 size = DATA_SIZE - cdr.FifoOffset;
1604 if (size > max_words * 4)
1605 size = max_words * 4;
1608 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1609 cdr.FifoOffset += size;
1611 if (size < cdsize) {
1612 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1613 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1615 psxCpu->Clear(madr, cdsize / 4);
1617 CDRDMA_INT((cdsize/4) * 24);
1619 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1621 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1622 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1626 psxRegs.cycle += (cdsize/4) * 24 - 20;
1631 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1635 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1639 void cdrDmaInterrupt(void)
1641 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1643 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1648 static void getCdInfo(void)
1652 CDR_getTN(cdr.ResultTN);
1653 CDR_getTD(0, cdr.SetSectorEnd);
1654 tmp = cdr.SetSectorEnd[0];
1655 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1656 cdr.SetSectorEnd[2] = tmp;
1660 memset(&cdr, 0, sizeof(cdr));
1666 cdr.FifoOffset = DATA_SIZE; // fifo empty
1667 if (CdromId[0] == '\0') {
1668 cdr.DriveState = DRIVESTATE_STOPPED;
1672 cdr.DriveState = DRIVESTATE_STANDBY;
1673 cdr.StatP = STATUS_ROTATING;
1676 // BIOS player - default values
1677 cdr.AttenuatorLeftToLeft = 0x80;
1678 cdr.AttenuatorLeftToRight = 0x00;
1679 cdr.AttenuatorRightToLeft = 0x00;
1680 cdr.AttenuatorRightToRight = 0x80;
1685 int cdrFreeze(void *f, int Mode) {
1689 if (Mode == 0 && !Config.Cdda)
1692 cdr.freeze_ver = 0x63647202;
1693 gzfreeze(&cdr, sizeof(cdr));
1696 cdr.ParamP = cdr.ParamC;
1697 tmp = cdr.FifoOffset;
1700 gzfreeze(&tmp, sizeof(tmp));
1705 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1706 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1707 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1708 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1710 // read right sub data
1711 tmpp[0] = btoi(cdr.Prev[0]);
1712 tmpp[1] = btoi(cdr.Prev[1]);
1713 tmpp[2] = btoi(cdr.Prev[2]);
1718 if (cdr.freeze_ver < 0x63647202)
1719 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1721 Find_CurTrack(cdr.SetSectorPlay);
1723 CDR_play(cdr.SetSectorPlay);
1726 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1727 // old versions did not latch Reg2, have to fixup..
1728 if (cdr.Reg2 == 0) {
1729 SysPrintf("cdrom: fixing up old savestate\n");
1732 // also did not save Attenuator..
1733 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1734 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1736 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1744 void LidInterrupt(void) {
1746 cdrLidSeekInterrupt();