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 "psxevents.h"
30 #include "arm_features.h"
34 #define CDR_LOG SysPrintf
39 #define CDR_LOG_I SysPrintf
41 #define CDR_LOG_I(fmt, ...) \
42 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
45 #define CDR_LOG_IO SysPrintf
47 #define CDR_LOG_IO(...)
49 //#define CDR_LOG_CMD_IRQ
52 // unused members maintain savesate compatibility
53 unsigned char unused0;
54 unsigned char unused1;
56 unsigned char unused2;
62 unsigned char Transfer[DATA_SIZE];
66 unsigned char Relative[3];
67 unsigned char Absolute[3];
69 unsigned char TrackChanged;
70 unsigned char ReportDelay;
71 unsigned char unused3;
72 unsigned short sectorsRead;
73 unsigned int freeze_ver;
75 unsigned char Prev[4];
76 unsigned char Param[8];
77 unsigned char Result[16];
81 unsigned char ResultC;
82 unsigned char ResultP;
83 unsigned char ResultReady;
85 unsigned char SubqForwardSectors;
86 unsigned char SetlocPending;
89 unsigned char ResultTN[6];
90 unsigned char ResultTD[4];
91 unsigned char SetSectorPlay[4];
92 unsigned char SetSectorEnd[4];
93 unsigned char SetSector[4];
97 int Mode, File, Channel;
98 unsigned char LocL[8];
118 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
119 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
120 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
121 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
123 static s16 read_buf[CD_FRAMESIZE_RAW/2];
125 /* CD-ROM magic numbers */
126 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
131 #define CdlBackward 5
139 #define CdlSetfilter 13
140 #define CdlSetmode 14
141 #define CdlGetparam 15
142 #define CdlGetlocL 16
143 #define CdlGetlocP 17
149 #define CdlSetclock 23
150 #define CdlGetclock 24
156 #define CdlReadToc 30
158 #ifdef CDR_LOG_CMD_IRQ
159 static const char * const CmdName[0x100] = {
160 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
161 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
162 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
163 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
164 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
165 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
166 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
167 "CdlInit", NULL, "CDlReadToc", NULL
171 unsigned char Test04[] = { 0 };
172 unsigned char Test05[] = { 0 };
173 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
174 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
175 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
181 #define Acknowledge 3
186 #define MODE_SPEED (1<<7) // 0x80
187 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
188 #define MODE_SIZE_2340 (1<<5) // 0x20
189 #define MODE_SIZE_2328 (1<<4) // 0x10
190 #define MODE_SIZE_2048 (0<<4) // 0x00
191 #define MODE_SF (1<<3) // 0x08 channel on/off
192 #define MODE_REPORT (1<<2) // 0x04
193 #define MODE_AUTOPAUSE (1<<1) // 0x02
194 #define MODE_CDDA (1<<0) // 0x01
197 #define STATUS_PLAY (1<<7) // 0x80
198 #define STATUS_SEEK (1<<6) // 0x40
199 #define STATUS_READ (1<<5) // 0x20
200 #define STATUS_SHELLOPEN (1<<4) // 0x10
201 #define STATUS_UNKNOWN3 (1<<3) // 0x08
202 #define STATUS_UNKNOWN2 (1<<2) // 0x04
203 #define STATUS_ROTATING (1<<1) // 0x02
204 #define STATUS_ERROR (1<<0) // 0x01
207 #define ERROR_NOTREADY (1<<7) // 0x80
208 #define ERROR_INVALIDCMD (1<<6) // 0x40
209 #define ERROR_INVALIDARG (1<<5) // 0x20
211 // 1x = 75 sectors per second
212 // PSXCLK = 1 sec in the ps
213 // so (PSXCLK / 75) = cdr read time (linuzappz)
214 #define cdReadTime (PSXCLK / 75)
216 #define LOCL_INVALID 0xff
217 #define SUBQ_FORWARD_SECTORS 2u
220 DRIVESTATE_STANDBY = 0, // pause, play, read
222 DRIVESTATE_RESCAN_CD,
223 DRIVESTATE_PREPARE_CD,
227 static struct CdrStat stat;
229 static unsigned int msf2sec(const u8 *msf) {
230 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
233 // for that weird psemu API..
234 static unsigned int fsm2sec(const u8 *msf) {
235 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
238 static void sec2msf(unsigned int s, u8 *msf) {
239 msf[0] = s / 75 / 60;
240 s = s - msf[0] * 75 * 60;
246 // cdrPlayReadInterrupt
247 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
249 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
251 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
253 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
254 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
255 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
258 #define StopReading() { \
260 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
263 #define StopCdda() { \
264 if (cdr.Play && !Config.Cdda) CDR_stop(); \
266 cdr.FastForward = 0; \
267 cdr.FastBackward = 0; \
270 #define SetPlaySeekRead(x, f) { \
271 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
275 #define SetResultSize(size) { \
277 cdr.ResultC = size; \
278 cdr.ResultReady = 1; \
281 static void setIrq(int log_cmd)
283 if (cdr.Stat & cdr.Reg2)
284 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
286 #ifdef CDR_LOG_CMD_IRQ
290 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
291 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
292 for (i = 0; i < cdr.ResultC; i++)
293 SysPrintf("%02x ", cdr.Result[i]);
299 // timing used in this function was taken from tests on real hardware
300 // (yes it's slow, but you probably don't want to modify it)
301 void cdrLidSeekInterrupt(void)
303 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
305 switch (cdr.DriveState) {
307 case DRIVESTATE_STANDBY:
310 SetPlaySeekRead(cdr.StatP, 0);
312 if (CDR_getStatus(&stat) == -1)
315 if (stat.Status & STATUS_SHELLOPEN)
317 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
318 cdr.DriveState = DRIVESTATE_LID_OPEN;
319 set_event(PSXINT_CDRLID, 0x800);
323 case DRIVESTATE_LID_OPEN:
324 if (CDR_getStatus(&stat) == -1)
325 stat.Status &= ~STATUS_SHELLOPEN;
328 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
329 cdr.StatP |= STATUS_SHELLOPEN;
331 // could generate error irq here, but real hardware
332 // only sometimes does that
333 // (not done when lots of commands are sent?)
335 set_event(PSXINT_CDRLID, cdReadTime * 30);
338 else if (cdr.StatP & STATUS_ROTATING) {
339 cdr.StatP &= ~STATUS_ROTATING;
341 else if (!(stat.Status & STATUS_SHELLOPEN)) {
345 // cdr.StatP STATUS_SHELLOPEN is "sticky"
346 // and is only cleared by CdlNop
348 cdr.DriveState = DRIVESTATE_RESCAN_CD;
349 set_event(PSXINT_CDRLID, cdReadTime * 105);
354 set_event(PSXINT_CDRLID, cdReadTime * 3);
357 case DRIVESTATE_RESCAN_CD:
358 cdr.StatP |= STATUS_ROTATING;
359 cdr.DriveState = DRIVESTATE_PREPARE_CD;
361 // this is very long on real hardware, over 6 seconds
362 // make it a bit faster here...
363 set_event(PSXINT_CDRLID, cdReadTime * 150);
366 case DRIVESTATE_PREPARE_CD:
367 if (cdr.StatP & STATUS_SEEK) {
368 SetPlaySeekRead(cdr.StatP, 0);
369 cdr.DriveState = DRIVESTATE_STANDBY;
372 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
373 set_event(PSXINT_CDRLID, cdReadTime * 26);
379 static void Find_CurTrack(const u8 *time)
383 current = msf2sec(time);
385 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
386 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
387 sect = fsm2sec(cdr.ResultTD);
388 if (sect - current >= 150)
393 static void generate_subq(const u8 *time)
395 unsigned char start[3], next[3];
396 unsigned int this_s, start_s, next_s, pregap;
399 CDR_getTD(cdr.CurTrack, start);
400 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
402 CDR_getTD(cdr.CurTrack + 1, next);
405 // last track - cd size
407 next[0] = cdr.SetSectorEnd[2];
408 next[1] = cdr.SetSectorEnd[1];
409 next[2] = cdr.SetSectorEnd[0];
412 this_s = msf2sec(time);
413 start_s = fsm2sec(start);
414 next_s = fsm2sec(next);
416 cdr.TrackChanged = FALSE;
418 if (next_s - this_s < pregap) {
419 cdr.TrackChanged = TRUE;
426 relative_s = this_s - start_s;
427 if (relative_s < 0) {
429 relative_s = -relative_s;
431 sec2msf(relative_s, cdr.subq.Relative);
433 cdr.subq.Track = itob(cdr.CurTrack);
434 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
435 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
436 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
437 cdr.subq.Absolute[0] = itob(time[0]);
438 cdr.subq.Absolute[1] = itob(time[1]);
439 cdr.subq.Absolute[2] = itob(time[2]);
442 static int ReadTrack(const u8 *time)
444 unsigned char tmp[3];
447 tmp[0] = itob(time[0]);
448 tmp[1] = itob(time[1]);
449 tmp[2] = itob(time[2]);
451 if (memcmp(cdr.Prev, tmp, 3) == 0)
454 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
456 read_ok = CDR_readTrack(tmp);
458 memcpy(cdr.Prev, tmp, 3);
462 static void UpdateSubq(const u8 *time)
464 const struct SubQ *subq;
470 subq = (struct SubQ *)CDR_getBufferSub(MSF2SECT(time[0], time[1], time[2]));
471 if (subq != NULL && cdr.CurTrack == 1) {
472 crc = calcCrc((u8 *)subq + 12, 10);
473 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
474 cdr.subq.Track = subq->TrackNumber;
475 cdr.subq.Index = subq->IndexNumber;
476 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
477 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
480 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
481 time[0], time[1], time[2]);
488 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
489 cdr.subq.Track, cdr.subq.Index,
490 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
491 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
494 static void cdrPlayInterrupt_Autopause()
497 boolean abs_lev_chselect;
500 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
501 CDR_LOG( "CDDA STOP\n" );
503 // Magic the Gathering
504 // - looping territory cdda
507 //cdr.ResultReady = 1;
508 //cdr.Stat = DataReady;
510 setIrq(0x1000); // 0x1000 just for logging purposes
513 SetPlaySeekRead(cdr.StatP, 0);
515 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
516 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
518 cdr.Result[0] = cdr.StatP;
519 cdr.Result[1] = cdr.subq.Track;
520 cdr.Result[2] = cdr.subq.Index;
522 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
524 /* 8 is a hack. For accuracy, it should be 588. */
525 for (i = 0; i < 8; i++)
527 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
529 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
530 abs_lev_max |= abs_lev_chselect << 15;
532 if (cdr.subq.Absolute[2] & 0x10) {
533 cdr.Result[3] = cdr.subq.Relative[0];
534 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
535 cdr.Result[5] = cdr.subq.Relative[2];
538 cdr.Result[3] = cdr.subq.Absolute[0];
539 cdr.Result[4] = cdr.subq.Absolute[1];
540 cdr.Result[5] = cdr.subq.Absolute[2];
543 cdr.Result[6] = abs_lev_max >> 0;
544 cdr.Result[7] = abs_lev_max >> 8;
546 // Rayman: Logo freeze (resultready + dataready)
548 cdr.Stat = DataReady;
559 static int cdrSeekTime(unsigned char *target)
561 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
562 int pausePenalty, seekTime = abs(diff) * (cdReadTime / 2000);
563 seekTime = MAX_VALUE(seekTime, 20000);
565 // need this stupidly long penalty or else Spyro2 intro desyncs
566 pausePenalty = (s32)(psxRegs.cycle - cdr.LastReadCycles) > cdReadTime * 8 ? cdReadTime * 25 : 0;
567 seekTime += pausePenalty;
569 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
570 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
574 static u32 cdrAlignTimingHack(u32 cycles)
577 * timing hack for T'ai Fu - Wrath of the Tiger:
578 * The game has a bug where it issues some cdc commands from a low priority
579 * vint handler, however there is a higher priority default bios handler
580 * that acks the vint irq and returns, so game's handler is not reached
581 * (see bios irq handler chains at e004 and the game's irq handling func
582 * at 80036810). For the game to work, vint has to arrive after the bios
583 * vint handler rejects some other irq (of which only cd and rcnt2 are
584 * active), but before the game's handler loop reads I_STAT. The time
585 * window for this is quite small (~1k cycles of so). Apparently this
586 * somehow happens naturally on the real hardware.
588 * Note: always enforcing this breaks other games like Crash PAL version
589 * (inputs get dropped because bios handler doesn't see interrupts).
592 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
594 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
595 vint_rel += PSXCLK / 60;
596 while ((s32)(vint_rel - cycles) < 0)
597 vint_rel += PSXCLK / 60;
601 static void cdrUpdateTransferBuf(const u8 *buf);
602 static void cdrReadInterrupt(void);
603 static void cdrPrepCdda(s16 *buf, int samples);
604 static void cdrAttenuate(s16 *buf, int samples, int stereo);
606 static void msfiAdd(u8 *msfi, u32 count)
620 static void msfiSub(u8 *msfi, u32 count)
624 if ((s8)msfi[2] < 0) {
627 if ((s8)msfi[1] < 0) {
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;
831 CDR_play(cdr.SetSectorPlay);
833 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
835 // BIOS player - set flag again
838 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
843 // TODO: error 80 if stopped
846 // GameShark CD Player: Calls 2x + Play 2x
848 cdr.FastBackward = 0;
854 // GameShark CD Player: Calls 2x + Play 2x
855 cdr.FastBackward = 1;
860 if (cdr.DriveState != DRIVESTATE_STOPPED) {
861 error = ERROR_INVALIDARG;
864 second_resp_time = cdReadTime * 125 / 2;
868 case CdlStandby + CMD_PART2:
874 // grab time for current track
875 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
877 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
878 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
879 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
884 SetPlaySeekRead(cdr.StatP, 0);
885 cdr.StatP &= ~STATUS_ROTATING;
886 cdr.LocL[0] = LOCL_INVALID;
888 second_resp_time = 0x800;
889 if (cdr.DriveState == DRIVESTATE_STANDBY)
890 second_resp_time = cdReadTime * 30 / 2;
892 cdr.DriveState = DRIVESTATE_STOPPED;
895 case CdlStop + CMD_PART2:
903 // how the drive maintains the position while paused is quite
904 // complicated, this is the minimum to make "Bedlam" happy
905 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
909 Gundam Battle Assault 2: much slower (*)
910 - Fixes boot, gameplay
912 Hokuto no Ken 2: slower
913 - Fixes intro + subtitles
915 InuYasha - Feudal Fairy Tale: slower
918 /* Gameblabla - Tightening the timings (as taken from Duckstation).
919 * The timings from Duckstation are based upon hardware tests.
920 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
921 * seems to be timing sensitive as it can depend on the CPU's clock speed.
923 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
925 second_resp_time = 7000;
929 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
931 SetPlaySeekRead(cdr.StatP, 0);
934 case CdlPause + CMD_PART2:
939 case CdlReset + CMD_WHILE_NOT_READY:
942 SetPlaySeekRead(cdr.StatP, 0);
943 cdr.LocL[0] = LOCL_INVALID;
945 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
946 second_resp_time = not_ready ? 70000 : 4100000;
950 case CdlReset + CMD_PART2:
951 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
964 cdr.File = cdr.Param[0];
965 cdr.Channel = cdr.Param[1];
969 case CdlSetmode + CMD_WHILE_NOT_READY:
970 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
971 cdr.Mode = cdr.Param[0];
975 case CdlGetparam + CMD_WHILE_NOT_READY:
976 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
978 cdr.Result[1] = cdr.Mode;
980 cdr.Result[3] = cdr.File;
981 cdr.Result[4] = cdr.Channel;
985 if (cdr.LocL[0] == LOCL_INVALID) {
990 memcpy(cdr.Result, cdr.LocL, 8);
995 memcpy(&cdr.Result, &cdr.subq, 8);
998 case CdlReadT: // SetSession?
1000 second_resp_time = cdReadTime * 290 / 4;
1004 case CdlReadT + CMD_PART2:
1005 cdr.Stat = Complete;
1010 if (CDR_getTN(cdr.ResultTN) == -1) {
1011 cdr.Stat = DiskError;
1012 cdr.Result[0] |= STATUS_ERROR;
1014 cdr.Stat = Acknowledge;
1015 cdr.Result[1] = itob(cdr.ResultTN[0]);
1016 cdr.Result[2] = itob(cdr.ResultTN[1]);
1021 cdr.Track = btoi(cdr.Param[0]);
1023 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1024 cdr.Stat = DiskError;
1025 cdr.Result[0] |= STATUS_ERROR;
1027 cdr.Stat = Acknowledge;
1028 cdr.Result[0] = cdr.StatP;
1029 cdr.Result[1] = itob(cdr.ResultTD[2]);
1030 cdr.Result[2] = itob(cdr.ResultTD[1]);
1031 /* According to Nocash's documentation, the function doesn't care about ff.
1032 * This can be seen also in Mednafen's implementation. */
1033 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1041 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1043 seekTime = cdrSeekTime(cdr.SetSector);
1044 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1046 Crusaders of Might and Magic = 0.5x-4x
1047 - fix cutscene speech start
1049 Eggs of Steel = 2x-?
1053 - fix cutscene speech
1058 second_resp_time = cdReadTime + seekTime;
1062 case CdlSeekL + CMD_PART2:
1063 case CdlSeekP + CMD_PART2:
1064 SetPlaySeekRead(cdr.StatP, 0);
1065 cdr.Result[0] = cdr.StatP;
1066 cdr.Stat = Complete;
1068 Find_CurTrack(cdr.SetSectorPlay);
1069 read_ok = ReadTrack(cdr.SetSectorPlay);
1070 if (read_ok && (buf = CDR_getBuffer()))
1071 memcpy(cdr.LocL, buf, 8);
1072 UpdateSubq(cdr.SetSectorPlay);
1073 cdr.TrackChanged = FALSE;
1077 case CdlTest + CMD_WHILE_NOT_READY:
1078 switch (cdr.Param[0]) {
1079 case 0x20: // System Controller ROM Version
1081 memcpy(cdr.Result, Test20, 4);
1085 memcpy(cdr.Result, Test22, 4);
1087 case 0x23: case 0x24:
1089 memcpy(cdr.Result, Test23, 4);
1095 second_resp_time = 20480;
1098 case CdlID + CMD_PART2:
1100 cdr.Result[0] = cdr.StatP;
1105 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1106 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1107 cdr.Result[1] = 0xc0;
1111 cdr.Result[1] |= 0x10;
1112 if (CdromId[0] == '\0')
1113 cdr.Result[1] |= 0x80;
1115 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1117 /* This adds the string "PCSX" in Playstation bios boot screen */
1118 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1119 cdr.Stat = Complete;
1123 case CdlInit + CMD_WHILE_NOT_READY:
1126 SetPlaySeekRead(cdr.StatP, 0);
1127 // yes, it really sets STATUS_SHELLOPEN
1128 cdr.StatP |= STATUS_SHELLOPEN;
1129 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1130 set_event(PSXINT_CDRLID, 20480);
1135 case CdlGetQ + CMD_WHILE_NOT_READY:
1139 case CdlReadToc + CMD_WHILE_NOT_READY:
1140 cdr.LocL[0] = LOCL_INVALID;
1141 second_resp_time = cdReadTime * 180 / 4;
1145 case CdlReadToc + CMD_PART2:
1146 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1147 cdr.Stat = Complete;
1152 if (cdr.Reading && !cdr.SetlocPending)
1155 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1157 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1158 // Read* acts as play for cdda tracks in cdda mode
1162 if (cdr.SetlocPending) {
1163 seekTime = cdrSeekTime(cdr.SetSector);
1164 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1165 cdr.SetlocPending = 0;
1168 cdr.FirstSector = 1;
1170 // Fighting Force 2 - update subq time immediately
1172 UpdateSubq(cdr.SetSectorPlay);
1173 cdr.LocL[0] = LOCL_INVALID;
1174 cdr.SubqForwardSectors = 1;
1175 cdr.sectorsRead = 0;
1177 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1179 if (Config.hacks.cdr_read_timing)
1180 cycles = cdrAlignTimingHack(cycles);
1181 CDRPLAYREAD_INT(cycles, 1);
1183 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1189 error = ERROR_INVALIDCMD;
1193 CDR_LOG_I("cmd %02x error %02x\n", Cmd, error);
1195 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1196 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1197 cdr.Stat = DiskError;
1201 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1202 cdr.DriveState = DRIVESTATE_STANDBY;
1203 cdr.StatP |= STATUS_ROTATING;
1206 if (second_resp_time) {
1207 cdr.CmdInProgress = Cmd | 0x100;
1208 set_event(PSXINT_CDR, second_resp_time);
1210 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1211 cdr.CmdInProgress = cdr.Cmd;
1212 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1219 #define ssat32_to_16(v) \
1220 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1222 #define ssat32_to_16(v) do { \
1223 if (v < -32768) v = -32768; \
1224 else if (v > 32767) v = 32767; \
1228 static void cdrPrepCdda(s16 *buf, int samples)
1230 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1232 for (i = 0; i < samples; i++) {
1233 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1234 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1239 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1242 int ll = cdr.AttenuatorLeftToLeft;
1243 int lr = cdr.AttenuatorLeftToRight;
1244 int rl = cdr.AttenuatorRightToLeft;
1245 int rr = cdr.AttenuatorRightToRight;
1247 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1250 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1254 for (i = 0; i < samples; i++) {
1257 l = (l * ll + r * rl) >> 7;
1258 r = (r * rr + l * lr) >> 7;
1266 for (i = 0; i < samples; i++) {
1268 l = l * (ll + rl) >> 7;
1269 //r = r * (rr + lr) >> 7;
1277 static void cdrReadInterruptSetResult(unsigned char result)
1280 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1281 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1282 cdr.CmdInProgress, cdr.Stat);
1283 cdr.Irq1Pending = result;
1287 cdr.Result[0] = result;
1288 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1292 static void cdrUpdateTransferBuf(const u8 *buf)
1296 memcpy(cdr.Transfer, buf, DATA_SIZE);
1297 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1298 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1299 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1300 if (cdr.FifoOffset < 2048 + 12)
1301 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1304 static void cdrReadInterrupt(void)
1306 u8 *buf = NULL, *hdr;
1310 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1311 msfiAdd(subqPos, cdr.SubqForwardSectors);
1312 UpdateSubq(subqPos);
1313 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1314 cdr.SubqForwardSectors++;
1315 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1319 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1320 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1323 read_ok = ReadTrack(cdr.SetSectorPlay);
1325 buf = CDR_getBuffer();
1330 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1331 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1334 memcpy(cdr.LocL, buf, 8);
1336 if (!cdr.Stat && !cdr.Irq1Pending)
1337 cdrUpdateTransferBuf(buf);
1339 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1341 // Firemen 2: Multi-XA files - briefings, cutscenes
1342 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1344 cdr.Channel = hdr[1];
1348 * Skips playing on channel 255.
1349 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1350 * TODO : Check if this is the proper behaviour.
1352 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1353 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1355 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1356 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1357 cdr.FirstSector = 0;
1359 else cdr.FirstSector = -1;
1364 Croc 2: $40 - only FORM1 (*)
1365 Judge Dredd: $C8 - only FORM1 (*)
1366 Sim Theme Park - no adpcm at all (zero)
1369 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1370 cdrReadInterruptSetResult(cdr.StatP);
1372 msfiAdd(cdr.SetSectorPlay, 1);
1374 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1383 bit 5 - 1 result ready
1385 bit 7 - 1 command being processed
1388 unsigned char cdrRead0(void) {
1389 if (cdr.ResultReady)
1394 cdr.Ctrl |= 0x40; // data fifo not empty
1396 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1399 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1401 return psxHu8(0x1800) = cdr.Ctrl;
1404 void cdrWrite0(unsigned char rt) {
1405 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1407 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1410 unsigned char cdrRead1(void) {
1411 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1412 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1416 if (cdr.ResultP == cdr.ResultC)
1417 cdr.ResultReady = 0;
1419 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1421 return psxHu8(0x1801);
1424 void cdrWrite1(unsigned char rt) {
1425 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1426 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1428 switch (cdr.Ctrl & 3) {
1432 cdr.AttenuatorRightToRightT = rt;
1438 #ifdef CDR_LOG_CMD_IRQ
1439 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1442 SysPrintf(" Param[%d] = {", cdr.ParamC);
1443 for (i = 0; i < cdr.ParamC; i++)
1444 SysPrintf(" %x,", cdr.Param[i]);
1451 cdr.ResultReady = 0;
1454 if (!cdr.CmdInProgress) {
1455 cdr.CmdInProgress = rt;
1456 // should be something like 12k + controller delays
1457 set_event(PSXINT_CDR, 5000);
1460 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1461 rt, cdr.Cmd, cdr.CmdInProgress);
1462 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1463 cdr.CmdInProgress = rt;
1469 unsigned char cdrRead2(void) {
1470 unsigned char ret = cdr.Transfer[0x920];
1472 if (cdr.FifoOffset < cdr.FifoSize)
1473 ret = cdr.Transfer[cdr.FifoOffset++];
1475 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1477 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1481 void cdrWrite2(unsigned char rt) {
1482 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1483 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1485 switch (cdr.Ctrl & 3) {
1487 if (cdr.ParamC < 8) // FIXME: size and wrapping
1488 cdr.Param[cdr.ParamC++] = rt;
1495 cdr.AttenuatorLeftToLeftT = rt;
1498 cdr.AttenuatorRightToLeftT = rt;
1503 unsigned char cdrRead3(void) {
1505 psxHu8(0x1803) = cdr.Stat | 0xE0;
1507 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1509 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1510 return psxHu8(0x1803);
1513 void cdrWrite3(unsigned char rt) {
1514 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1515 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1517 switch (cdr.Ctrl & 3) {
1521 if (cdr.Stat & rt) {
1522 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1523 + psxRegs.intCycle[PSXINT_CDR].cycle;
1524 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1525 #ifdef CDR_LOG_CMD_IRQ
1526 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1527 !!pending, cdr.CmdInProgress,
1528 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1530 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1531 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1534 if (cdr.CmdInProgress) {
1535 c = 2048 - (psxRegs.cycle - nextCycle);
1536 c = MAX_VALUE(c, 512);
1538 set_event(PSXINT_CDR, c);
1547 cdr.AttenuatorLeftToRightT = rt;
1551 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1552 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1553 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1554 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1560 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1561 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1563 else if (rt & 0x80) {
1564 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1565 case MODE_SIZE_2328:
1567 cdr.FifoOffset = 12;
1568 cdr.FifoSize = 2048 + 12;
1571 case MODE_SIZE_2340:
1574 cdr.FifoSize = 2340;
1578 else if (!(rt & 0xc0))
1579 cdr.FifoOffset = DATA_SIZE; // fifo empty
1582 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1583 u32 cdsize, max_words;
1588 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1589 if (cdr.FifoOffset == 0) {
1591 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1596 switch (chcr & 0x71000000) {
1598 ptr = getDmaRam(madr, &max_words);
1599 if (ptr == INVALID_PTR) {
1600 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1604 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1607 GS CDX: Enhancement CD crash
1610 - Spams DMA3 and gets buffer overrun
1612 size = DATA_SIZE - cdr.FifoOffset;
1615 if (size > max_words * 4)
1616 size = max_words * 4;
1619 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1620 cdr.FifoOffset += size;
1622 if (size < cdsize) {
1623 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1624 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1626 psxCpu->Clear(madr, cdsize / 4);
1628 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1630 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1632 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1633 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1637 psxRegs.cycle += (cdsize/4) * 24 - 20;
1642 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1646 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1650 void cdrDmaInterrupt(void)
1652 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1654 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1659 static void getCdInfo(void)
1663 CDR_getTN(cdr.ResultTN);
1664 CDR_getTD(0, cdr.SetSectorEnd);
1665 tmp = cdr.SetSectorEnd[0];
1666 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1667 cdr.SetSectorEnd[2] = tmp;
1671 memset(&cdr, 0, sizeof(cdr));
1677 cdr.FifoOffset = DATA_SIZE; // fifo empty
1678 if (CdromId[0] == '\0') {
1679 cdr.DriveState = DRIVESTATE_STOPPED;
1683 cdr.DriveState = DRIVESTATE_STANDBY;
1684 cdr.StatP = STATUS_ROTATING;
1687 // BIOS player - default values
1688 cdr.AttenuatorLeftToLeft = 0x80;
1689 cdr.AttenuatorLeftToRight = 0x00;
1690 cdr.AttenuatorRightToLeft = 0x00;
1691 cdr.AttenuatorRightToRight = 0x80;
1696 int cdrFreeze(void *f, int Mode) {
1700 if (Mode == 0 && !Config.Cdda)
1703 cdr.freeze_ver = 0x63647202;
1704 gzfreeze(&cdr, sizeof(cdr));
1707 cdr.ParamP = cdr.ParamC;
1708 tmp = cdr.FifoOffset;
1711 gzfreeze(&tmp, sizeof(tmp));
1716 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1717 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1718 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1719 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1721 // read right sub data
1722 tmpp[0] = btoi(cdr.Prev[0]);
1723 tmpp[1] = btoi(cdr.Prev[1]);
1724 tmpp[2] = btoi(cdr.Prev[2]);
1729 if (cdr.freeze_ver < 0x63647202)
1730 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1732 Find_CurTrack(cdr.SetSectorPlay);
1734 CDR_play(cdr.SetSectorPlay);
1737 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1738 // old versions did not latch Reg2, have to fixup..
1739 if (cdr.Reg2 == 0) {
1740 SysPrintf("cdrom: fixing up old savestate\n");
1743 // also did not save Attenuator..
1744 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1745 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1747 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1755 void LidInterrupt(void) {
1757 cdrLidSeekInterrupt();