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];
109 u32 LastReadSeekCycles;
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 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
453 if (memcmp(cdr.Prev, tmp, 3) == 0)
456 read_ok = CDR_readTrack(tmp);
458 memcpy(cdr.Prev, tmp, 3);
462 static void UpdateSubq(const u8 *time)
464 const struct SubQ *subq;
465 int s = MSF2SECT(time[0], time[1], time[2]);
471 subq = (struct SubQ *)CDR_getBufferSub(s);
472 if (subq != NULL && cdr.CurTrack == 1) {
473 crc = calcCrc((u8 *)subq + 12, 10);
474 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
475 cdr.subq.Track = subq->TrackNumber;
476 cdr.subq.Index = subq->IndexNumber;
477 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
478 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
481 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
482 time[0], time[1], time[2]);
489 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
490 cdr.subq.Track, cdr.subq.Index,
491 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
492 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
495 static void cdrPlayInterrupt_Autopause()
498 boolean abs_lev_chselect;
501 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
502 CDR_LOG( "CDDA STOP\n" );
504 // Magic the Gathering
505 // - looping territory cdda
508 //cdr.ResultReady = 1;
509 //cdr.Stat = DataReady;
511 setIrq(0x1000); // 0x1000 just for logging purposes
514 SetPlaySeekRead(cdr.StatP, 0);
516 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
517 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
519 cdr.Result[0] = cdr.StatP;
520 cdr.Result[1] = cdr.subq.Track;
521 cdr.Result[2] = cdr.subq.Index;
523 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
525 /* 8 is a hack. For accuracy, it should be 588. */
526 for (i = 0; i < 8; i++)
528 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
530 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
531 abs_lev_max |= abs_lev_chselect << 15;
533 if (cdr.subq.Absolute[2] & 0x10) {
534 cdr.Result[3] = cdr.subq.Relative[0];
535 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
536 cdr.Result[5] = cdr.subq.Relative[2];
539 cdr.Result[3] = cdr.subq.Absolute[0];
540 cdr.Result[4] = cdr.subq.Absolute[1];
541 cdr.Result[5] = cdr.subq.Absolute[2];
544 cdr.Result[6] = abs_lev_max >> 0;
545 cdr.Result[7] = abs_lev_max >> 8;
547 // Rayman: Logo freeze (resultready + dataready)
549 cdr.Stat = DataReady;
559 static int cdrSeekTime(unsigned char *target)
561 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
562 int seekTime = abs(diff) * (cdReadTime / 2000);
563 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
564 seekTime = MAX_VALUE(seekTime, 20000);
566 // need this stupidly long penalty or else Spyro2 intro desyncs
567 // note: if misapplied this breaks MGS cutscenes among other things
568 if (cyclesSinceRS > cdReadTime * 50)
569 seekTime += cdReadTime * 25;
570 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
571 // and then wants some slack time
572 else if (cyclesSinceRS < cdReadTime *3/2)
573 seekTime += cdReadTime;
575 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
576 CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
577 (float)seekTime / cdReadTime, (float)cyclesSinceRS / 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 static void msfiSub(u8 *msfi, u32 count)
631 if ((s8)msfi[2] < 0) {
634 if ((s8)msfi[1] < 0) {
641 void cdrPlayReadInterrupt(void)
643 cdr.LastReadSeekCycles = psxRegs.cycle;
650 if (!cdr.Play) return;
652 CDR_LOG( "CDDA - %d:%d:%d\n",
653 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
655 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
656 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
658 SetPlaySeekRead(cdr.StatP, 0);
659 cdr.TrackChanged = TRUE;
662 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
665 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
666 cdrPlayInterrupt_Autopause();
668 if (!cdr.Muted && !Config.Cdda) {
669 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
670 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
671 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
675 msfiAdd(cdr.SetSectorPlay, 1);
677 // update for CdlGetlocP/autopause
678 generate_subq(cdr.SetSectorPlay);
680 CDRPLAYREAD_INT(cdReadTime, 0);
683 #define CMD_PART2 0x100
684 #define CMD_WHILE_NOT_READY 0x200
686 void cdrInterrupt(void) {
687 int start_rotating = 0;
689 u32 cycles, seekTime = 0;
690 u32 second_resp_time = 0;
700 CDR_LOG_I("cmd %02x with irqstat %x\n",
701 cdr.CmdInProgress, cdr.Stat);
704 if (cdr.Irq1Pending) {
705 // hand out the "newest" sector, according to nocash
706 cdrUpdateTransferBuf(CDR_getBuffer());
707 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
708 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
709 cdr.CmdInProgress, cdr.Irq1Pending);
711 cdr.Result[0] = cdr.Irq1Pending;
712 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
720 cdr.Result[0] = cdr.StatP;
721 cdr.Stat = Acknowledge;
723 Cmd = cdr.CmdInProgress;
724 cdr.CmdInProgress = 0;
733 switch (cdr.DriveState) {
734 case DRIVESTATE_PREPARE_CD:
736 // Syphon filter 2 expects commands to work shortly after it sees
737 // STATUS_ROTATING, so give up trying to emulate the startup seq
738 cdr.DriveState = DRIVESTATE_STANDBY;
739 cdr.StatP &= ~STATUS_SEEK;
740 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
744 case DRIVESTATE_LID_OPEN:
745 case DRIVESTATE_RESCAN_CD:
746 // no disk or busy with the initial scan, allowed cmds are limited
747 not_ready = CMD_WHILE_NOT_READY;
751 switch (Cmd | not_ready) {
753 case CdlNop + CMD_WHILE_NOT_READY:
754 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
755 cdr.StatP &= ~STATUS_SHELLOPEN;
759 case CdlSetloc + CMD_WHILE_NOT_READY:
760 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
762 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
763 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))
765 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
766 if (++cdr.errorRetryhack > 100)
768 error = ERROR_INVALIDARG;
773 for (i = 0; i < 3; i++)
774 set_loc[i] = btoi(cdr.Param[i]);
775 memcpy(cdr.SetSector, set_loc, 3);
776 cdr.SetSector[3] = 0;
777 cdr.SetlocPending = 1;
778 cdr.errorRetryhack = 0;
787 cdr.FastBackward = 0;
791 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
793 if (ParamC != 0 && cdr.Param[0] != 0) {
794 int track = btoi( cdr.Param[0] );
796 if (track <= cdr.ResultTN[1])
797 cdr.CurTrack = track;
799 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
801 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
802 for (i = 0; i < 3; i++)
803 set_loc[i] = cdr.ResultTD[2 - i];
804 seekTime = cdrSeekTime(set_loc);
805 memcpy(cdr.SetSectorPlay, set_loc, 3);
808 else if (cdr.SetlocPending) {
809 seekTime = cdrSeekTime(cdr.SetSector);
810 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
813 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
814 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
816 cdr.SetlocPending = 0;
819 Rayman: detect track changes
822 Twisted Metal 2: skip PREGAP + starting accurate SubQ
823 - plays tracks without retry play
825 Wild 9: skip PREGAP + starting accurate SubQ
826 - plays tracks without retry play
828 Find_CurTrack(cdr.SetSectorPlay);
829 generate_subq(cdr.SetSectorPlay);
830 cdr.LocL[0] = LOCL_INVALID;
831 cdr.SubqForwardSectors = 1;
832 cdr.TrackChanged = FALSE;
834 cdr.ReportDelay = 60;
838 CDR_play(cdr.SetSectorPlay);
840 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
842 // BIOS player - set flag again
845 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
850 // TODO: error 80 if stopped
853 // GameShark CD Player: Calls 2x + Play 2x
855 cdr.FastBackward = 0;
861 // GameShark CD Player: Calls 2x + Play 2x
862 cdr.FastBackward = 1;
867 if (cdr.DriveState != DRIVESTATE_STOPPED) {
868 error = ERROR_INVALIDARG;
871 second_resp_time = cdReadTime * 125 / 2;
875 case CdlStandby + CMD_PART2:
881 // grab time for current track
882 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
884 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
885 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
886 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
891 SetPlaySeekRead(cdr.StatP, 0);
892 cdr.StatP &= ~STATUS_ROTATING;
893 cdr.LocL[0] = LOCL_INVALID;
895 second_resp_time = 0x800;
896 if (cdr.DriveState == DRIVESTATE_STANDBY)
897 second_resp_time = cdReadTime * 30 / 2;
899 cdr.DriveState = DRIVESTATE_STOPPED;
902 case CdlStop + CMD_PART2:
910 // how the drive maintains the position while paused is quite
911 // complicated, this is the minimum to make "Bedlam" happy
912 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
916 Gundam Battle Assault 2: much slower (*)
917 - Fixes boot, gameplay
919 Hokuto no Ken 2: slower
920 - Fixes intro + subtitles
922 InuYasha - Feudal Fairy Tale: slower
925 /* Gameblabla - Tightening the timings (as taken from Duckstation).
926 * The timings from Duckstation are based upon hardware tests.
927 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
928 * seems to be timing sensitive as it can depend on the CPU's clock speed.
930 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
932 second_resp_time = 7000;
936 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
938 SetPlaySeekRead(cdr.StatP, 0);
941 case CdlPause + CMD_PART2:
946 case CdlReset + CMD_WHILE_NOT_READY:
949 SetPlaySeekRead(cdr.StatP, 0);
950 cdr.LocL[0] = LOCL_INVALID;
952 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
953 second_resp_time = not_ready ? 70000 : 4100000;
957 case CdlReset + CMD_PART2:
958 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
971 cdr.File = cdr.Param[0];
972 cdr.Channel = cdr.Param[1];
976 case CdlSetmode + CMD_WHILE_NOT_READY:
977 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
978 cdr.Mode = cdr.Param[0];
982 case CdlGetparam + CMD_WHILE_NOT_READY:
983 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
985 cdr.Result[1] = cdr.Mode;
987 cdr.Result[3] = cdr.File;
988 cdr.Result[4] = cdr.Channel;
992 if (cdr.LocL[0] == LOCL_INVALID) {
997 memcpy(cdr.Result, cdr.LocL, 8);
1002 memcpy(&cdr.Result, &cdr.subq, 8);
1005 case CdlReadT: // SetSession?
1007 second_resp_time = cdReadTime * 290 / 4;
1011 case CdlReadT + CMD_PART2:
1012 cdr.Stat = Complete;
1017 if (CDR_getTN(cdr.ResultTN) == -1) {
1018 cdr.Stat = DiskError;
1019 cdr.Result[0] |= STATUS_ERROR;
1021 cdr.Stat = Acknowledge;
1022 cdr.Result[1] = itob(cdr.ResultTN[0]);
1023 cdr.Result[2] = itob(cdr.ResultTN[1]);
1028 cdr.Track = btoi(cdr.Param[0]);
1030 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1031 cdr.Stat = DiskError;
1032 cdr.Result[0] |= STATUS_ERROR;
1034 cdr.Stat = Acknowledge;
1035 cdr.Result[0] = cdr.StatP;
1036 cdr.Result[1] = itob(cdr.ResultTD[2]);
1037 cdr.Result[2] = itob(cdr.ResultTD[1]);
1038 /* According to Nocash's documentation, the function doesn't care about ff.
1039 * This can be seen also in Mednafen's implementation. */
1040 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1048 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1050 seekTime = cdrSeekTime(cdr.SetSector);
1051 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1053 Crusaders of Might and Magic = 0.5x-4x
1054 - fix cutscene speech start
1056 Eggs of Steel = 2x-?
1060 - fix cutscene speech
1065 second_resp_time = cdReadTime + seekTime;
1069 case CdlSeekL + CMD_PART2:
1070 case CdlSeekP + CMD_PART2:
1071 SetPlaySeekRead(cdr.StatP, 0);
1072 cdr.Result[0] = cdr.StatP;
1073 cdr.Stat = Complete;
1075 Find_CurTrack(cdr.SetSectorPlay);
1076 read_ok = ReadTrack(cdr.SetSectorPlay);
1077 if (read_ok && (buf = CDR_getBuffer()))
1078 memcpy(cdr.LocL, buf, 8);
1079 UpdateSubq(cdr.SetSectorPlay);
1080 cdr.TrackChanged = FALSE;
1081 cdr.LastReadSeekCycles = psxRegs.cycle;
1085 case CdlTest + CMD_WHILE_NOT_READY:
1086 switch (cdr.Param[0]) {
1087 case 0x20: // System Controller ROM Version
1089 memcpy(cdr.Result, Test20, 4);
1093 memcpy(cdr.Result, Test22, 4);
1095 case 0x23: case 0x24:
1097 memcpy(cdr.Result, Test23, 4);
1103 second_resp_time = 20480;
1106 case CdlID + CMD_PART2:
1108 cdr.Result[0] = cdr.StatP;
1113 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1114 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1115 cdr.Result[1] = 0xc0;
1119 cdr.Result[1] |= 0x10;
1120 if (CdromId[0] == '\0')
1121 cdr.Result[1] |= 0x80;
1123 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1125 /* This adds the string "PCSX" in Playstation bios boot screen */
1126 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1127 cdr.Stat = Complete;
1131 case CdlInit + CMD_WHILE_NOT_READY:
1134 SetPlaySeekRead(cdr.StatP, 0);
1135 // yes, it really sets STATUS_SHELLOPEN
1136 cdr.StatP |= STATUS_SHELLOPEN;
1137 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1138 set_event(PSXINT_CDRLID, 20480);
1143 case CdlGetQ + CMD_WHILE_NOT_READY:
1147 case CdlReadToc + CMD_WHILE_NOT_READY:
1148 cdr.LocL[0] = LOCL_INVALID;
1149 second_resp_time = cdReadTime * 180 / 4;
1153 case CdlReadToc + CMD_PART2:
1154 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1155 cdr.Stat = Complete;
1160 if (cdr.Reading && !cdr.SetlocPending)
1163 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1165 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1166 // Read* acts as play for cdda tracks in cdda mode
1170 if (cdr.SetlocPending) {
1171 seekTime = cdrSeekTime(cdr.SetSector);
1172 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1173 cdr.SetlocPending = 0;
1176 cdr.FirstSector = 1;
1178 // Fighting Force 2 - update subq time immediately
1180 UpdateSubq(cdr.SetSectorPlay);
1181 cdr.LocL[0] = LOCL_INVALID;
1182 cdr.SubqForwardSectors = 1;
1183 cdr.sectorsRead = 0;
1185 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1187 if (Config.hacks.cdr_read_timing)
1188 cycles = cdrAlignTimingHack(cycles);
1189 CDRPLAYREAD_INT(cycles, 1);
1191 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1197 error = ERROR_INVALIDCMD;
1202 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1203 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1204 cdr.Stat = DiskError;
1205 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1209 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1210 cdr.DriveState = DRIVESTATE_STANDBY;
1211 cdr.StatP |= STATUS_ROTATING;
1214 if (second_resp_time) {
1215 cdr.CmdInProgress = Cmd | 0x100;
1216 set_event(PSXINT_CDR, second_resp_time);
1218 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1219 cdr.CmdInProgress = cdr.Cmd;
1220 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1227 #define ssat32_to_16(v) \
1228 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1230 #define ssat32_to_16(v) do { \
1231 if (v < -32768) v = -32768; \
1232 else if (v > 32767) v = 32767; \
1236 static void cdrPrepCdda(s16 *buf, int samples)
1238 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1240 for (i = 0; i < samples; i++) {
1241 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1242 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1247 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1250 int ll = cdr.AttenuatorLeftToLeft;
1251 int lr = cdr.AttenuatorLeftToRight;
1252 int rl = cdr.AttenuatorRightToLeft;
1253 int rr = cdr.AttenuatorRightToRight;
1255 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1258 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1262 for (i = 0; i < samples; i++) {
1265 l = (l * ll + r * rl) >> 7;
1266 r = (r * rr + l * lr) >> 7;
1274 for (i = 0; i < samples; i++) {
1276 l = l * (ll + rl) >> 7;
1277 //r = r * (rr + lr) >> 7;
1285 static void cdrReadInterruptSetResult(unsigned char result)
1288 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1289 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1290 cdr.CmdInProgress, cdr.Stat);
1291 cdr.Irq1Pending = result;
1295 cdr.Result[0] = result;
1296 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1300 static void cdrUpdateTransferBuf(const u8 *buf)
1304 memcpy(cdr.Transfer, buf, DATA_SIZE);
1305 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1306 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1307 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1308 if (cdr.FifoOffset < 2048 + 12)
1309 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1312 static void cdrReadInterrupt(void)
1314 u8 *buf = NULL, *hdr;
1318 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1319 msfiAdd(subqPos, cdr.SubqForwardSectors);
1320 UpdateSubq(subqPos);
1321 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1322 cdr.SubqForwardSectors++;
1323 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1327 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1328 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1331 read_ok = ReadTrack(cdr.SetSectorPlay);
1333 buf = CDR_getBuffer();
1338 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1339 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1342 memcpy(cdr.LocL, buf, 8);
1344 if (!cdr.Stat && !cdr.Irq1Pending)
1345 cdrUpdateTransferBuf(buf);
1347 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1349 // Firemen 2: Multi-XA files - briefings, cutscenes
1350 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1352 cdr.Channel = hdr[1];
1356 * Skips playing on channel 255.
1357 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1358 * TODO : Check if this is the proper behaviour.
1360 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1361 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1363 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1364 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1365 cdr.FirstSector = 0;
1367 else cdr.FirstSector = -1;
1372 Croc 2: $40 - only FORM1 (*)
1373 Judge Dredd: $C8 - only FORM1 (*)
1374 Sim Theme Park - no adpcm at all (zero)
1377 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1378 cdrReadInterruptSetResult(cdr.StatP);
1380 msfiAdd(cdr.SetSectorPlay, 1);
1382 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1391 bit 5 - 1 result ready
1393 bit 7 - 1 command being processed
1396 unsigned char cdrRead0(void) {
1397 if (cdr.ResultReady)
1402 cdr.Ctrl |= 0x40; // data fifo not empty
1404 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1407 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1409 return psxHu8(0x1800) = cdr.Ctrl;
1412 void cdrWrite0(unsigned char rt) {
1413 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1415 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1418 unsigned char cdrRead1(void) {
1419 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1420 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1424 if (cdr.ResultP == cdr.ResultC)
1425 cdr.ResultReady = 0;
1427 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1429 return psxHu8(0x1801);
1432 void cdrWrite1(unsigned char rt) {
1433 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1434 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1436 switch (cdr.Ctrl & 3) {
1440 cdr.AttenuatorRightToRightT = rt;
1446 #ifdef CDR_LOG_CMD_IRQ
1447 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1450 SysPrintf(" Param[%d] = {", cdr.ParamC);
1451 for (i = 0; i < cdr.ParamC; i++)
1452 SysPrintf(" %x,", cdr.Param[i]);
1459 cdr.ResultReady = 0;
1462 if (!cdr.CmdInProgress) {
1463 cdr.CmdInProgress = rt;
1464 // should be something like 12k + controller delays
1465 set_event(PSXINT_CDR, 5000);
1468 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1469 rt, cdr.Cmd, cdr.CmdInProgress);
1470 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1471 cdr.CmdInProgress = rt;
1477 unsigned char cdrRead2(void) {
1478 unsigned char ret = cdr.Transfer[0x920];
1480 if (cdr.FifoOffset < cdr.FifoSize)
1481 ret = cdr.Transfer[cdr.FifoOffset++];
1483 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1485 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1489 void cdrWrite2(unsigned char rt) {
1490 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1491 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1493 switch (cdr.Ctrl & 3) {
1495 if (cdr.ParamC < 8) // FIXME: size and wrapping
1496 cdr.Param[cdr.ParamC++] = rt;
1503 cdr.AttenuatorLeftToLeftT = rt;
1506 cdr.AttenuatorRightToLeftT = rt;
1511 unsigned char cdrRead3(void) {
1513 psxHu8(0x1803) = cdr.Stat | 0xE0;
1515 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1517 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1518 return psxHu8(0x1803);
1521 void cdrWrite3(unsigned char rt) {
1522 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1523 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1525 switch (cdr.Ctrl & 3) {
1529 if (cdr.Stat & rt) {
1530 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1531 + psxRegs.intCycle[PSXINT_CDR].cycle;
1532 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1533 #ifdef CDR_LOG_CMD_IRQ
1534 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1535 !!pending, cdr.CmdInProgress,
1536 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1538 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1539 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1542 if (cdr.CmdInProgress) {
1543 c = 2048 - (psxRegs.cycle - nextCycle);
1544 c = MAX_VALUE(c, 512);
1546 set_event(PSXINT_CDR, c);
1555 cdr.AttenuatorLeftToRightT = rt;
1559 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1560 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1561 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1562 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1568 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1569 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1571 else if (rt & 0x80) {
1572 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1573 case MODE_SIZE_2328:
1575 cdr.FifoOffset = 12;
1576 cdr.FifoSize = 2048 + 12;
1579 case MODE_SIZE_2340:
1582 cdr.FifoSize = 2340;
1586 else if (!(rt & 0xc0))
1587 cdr.FifoOffset = DATA_SIZE; // fifo empty
1590 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1591 u32 cdsize, max_words;
1596 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1597 if (cdr.FifoOffset == 0) {
1599 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1604 switch (chcr & 0x71000000) {
1606 ptr = getDmaRam(madr, &max_words);
1607 if (ptr == INVALID_PTR) {
1608 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1612 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1615 GS CDX: Enhancement CD crash
1618 - Spams DMA3 and gets buffer overrun
1620 size = DATA_SIZE - cdr.FifoOffset;
1623 if (size > max_words * 4)
1624 size = max_words * 4;
1627 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1628 cdr.FifoOffset += size;
1630 if (size < cdsize) {
1631 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1632 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1634 psxCpu->Clear(madr, cdsize / 4);
1636 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1638 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1640 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1641 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1645 psxRegs.cycle += (cdsize/4) * 24 - 20;
1650 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1654 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1658 void cdrDmaInterrupt(void)
1660 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1662 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1667 static void getCdInfo(void)
1671 CDR_getTN(cdr.ResultTN);
1672 CDR_getTD(0, cdr.SetSectorEnd);
1673 tmp = cdr.SetSectorEnd[0];
1674 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1675 cdr.SetSectorEnd[2] = tmp;
1679 memset(&cdr, 0, sizeof(cdr));
1685 cdr.FifoOffset = DATA_SIZE; // fifo empty
1686 if (CdromId[0] == '\0') {
1687 cdr.DriveState = DRIVESTATE_STOPPED;
1691 cdr.DriveState = DRIVESTATE_STANDBY;
1692 cdr.StatP = STATUS_ROTATING;
1695 // BIOS player - default values
1696 cdr.AttenuatorLeftToLeft = 0x80;
1697 cdr.AttenuatorLeftToRight = 0x00;
1698 cdr.AttenuatorRightToLeft = 0x00;
1699 cdr.AttenuatorRightToRight = 0x80;
1704 int cdrFreeze(void *f, int Mode) {
1708 if (Mode == 0 && !Config.Cdda)
1711 cdr.freeze_ver = 0x63647202;
1712 gzfreeze(&cdr, sizeof(cdr));
1715 cdr.ParamP = cdr.ParamC;
1716 tmp = cdr.FifoOffset;
1719 gzfreeze(&tmp, sizeof(tmp));
1724 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1725 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1726 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1727 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1729 // read right sub data
1730 tmpp[0] = btoi(cdr.Prev[0]);
1731 tmpp[1] = btoi(cdr.Prev[1]);
1732 tmpp[2] = btoi(cdr.Prev[2]);
1737 if (cdr.freeze_ver < 0x63647202)
1738 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1740 Find_CurTrack(cdr.SetSectorPlay);
1742 CDR_play(cdr.SetSectorPlay);
1745 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1746 // old versions did not latch Reg2, have to fixup..
1747 if (cdr.Reg2 == 0) {
1748 SysPrintf("cdrom: fixing up old savestate\n");
1751 // also did not save Attenuator..
1752 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1753 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1755 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1763 void LidInterrupt(void) {
1765 cdrLidSeekInterrupt();