1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
18 ***************************************************************************/
21 * Handles all CD-ROM registers and functions.
29 #include "arm_features.h"
33 #define CDR_LOG SysPrintf
38 #define CDR_LOG_I SysPrintf
40 #define CDR_LOG_I(fmt, ...) \
41 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
44 #define CDR_LOG_IO SysPrintf
46 #define CDR_LOG_IO(...)
48 //#define CDR_LOG_CMD_IRQ
51 // unused members maintain savesate compatibility
52 unsigned char unused0;
53 unsigned char unused1;
55 unsigned char unused2;
61 unsigned char Transfer[DATA_SIZE];
65 unsigned char Relative[3];
66 unsigned char Absolute[3];
68 unsigned char TrackChanged;
69 unsigned char ReportDelay;
70 unsigned char unused3;
71 unsigned short sectorsRead;
72 unsigned int freeze_ver;
74 unsigned char Prev[4];
75 unsigned char Param[8];
76 unsigned char Result[16];
80 unsigned char ResultC;
81 unsigned char ResultP;
82 unsigned char ResultReady;
84 unsigned char SubqForwardSectors;
85 unsigned char SetlocPending;
88 unsigned char ResultTN[6];
89 unsigned char ResultTD[4];
90 unsigned char SetSectorPlay[4];
91 unsigned char SetSectorEnd[4];
92 unsigned char SetSector[4];
96 int Mode, File, Channel;
97 unsigned char LocL[8];
117 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
118 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
119 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
120 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
122 static s16 read_buf[CD_FRAMESIZE_RAW/2];
124 /* CD-ROM magic numbers */
125 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
130 #define CdlBackward 5
138 #define CdlSetfilter 13
139 #define CdlSetmode 14
140 #define CdlGetparam 15
141 #define CdlGetlocL 16
142 #define CdlGetlocP 17
148 #define CdlSetclock 23
149 #define CdlGetclock 24
155 #define CdlReadToc 30
157 #ifdef CDR_LOG_CMD_IRQ
158 static const char * const CmdName[0x100] = {
159 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
160 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
161 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
162 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
163 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
164 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
165 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
166 "CdlInit", NULL, "CDlReadToc", NULL
170 unsigned char Test04[] = { 0 };
171 unsigned char Test05[] = { 0 };
172 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
173 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
174 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
180 #define Acknowledge 3
185 #define MODE_SPEED (1<<7) // 0x80
186 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
187 #define MODE_SIZE_2340 (1<<5) // 0x20
188 #define MODE_SIZE_2328 (1<<4) // 0x10
189 #define MODE_SIZE_2048 (0<<4) // 0x00
190 #define MODE_SF (1<<3) // 0x08 channel on/off
191 #define MODE_REPORT (1<<2) // 0x04
192 #define MODE_AUTOPAUSE (1<<1) // 0x02
193 #define MODE_CDDA (1<<0) // 0x01
196 #define STATUS_PLAY (1<<7) // 0x80
197 #define STATUS_SEEK (1<<6) // 0x40
198 #define STATUS_READ (1<<5) // 0x20
199 #define STATUS_SHELLOPEN (1<<4) // 0x10
200 #define STATUS_UNKNOWN3 (1<<3) // 0x08
201 #define STATUS_UNKNOWN2 (1<<2) // 0x04
202 #define STATUS_ROTATING (1<<1) // 0x02
203 #define STATUS_ERROR (1<<0) // 0x01
206 #define ERROR_NOTREADY (1<<7) // 0x80
207 #define ERROR_INVALIDCMD (1<<6) // 0x40
208 #define ERROR_INVALIDARG (1<<5) // 0x20
210 // 1x = 75 sectors per second
211 // PSXCLK = 1 sec in the ps
212 // so (PSXCLK / 75) = cdr read time (linuzappz)
213 #define cdReadTime (PSXCLK / 75)
215 #define LOCL_INVALID 0xff
216 #define SUBQ_FORWARD_SECTORS 2u
219 DRIVESTATE_STANDBY = 0, // pause, play, read
221 DRIVESTATE_RESCAN_CD,
222 DRIVESTATE_PREPARE_CD,
226 static struct CdrStat stat;
228 static unsigned int msf2sec(const u8 *msf) {
229 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
232 // for that weird psemu API..
233 static unsigned int fsm2sec(const u8 *msf) {
234 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
237 static void sec2msf(unsigned int s, u8 *msf) {
238 msf[0] = s / 75 / 60;
239 s = s - msf[0] * 75 * 60;
246 #define CDR_INT(eCycle) { \
247 psxRegs.interrupt |= (1 << PSXINT_CDR); \
248 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
249 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
250 new_dyna_set_event(PSXINT_CDR, eCycle); \
253 // cdrPlayReadInterrupt
254 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
256 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
258 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
260 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
261 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
262 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
265 // cdrLidSeekInterrupt
266 #define CDRLID_INT(eCycle) { \
267 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
268 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
269 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
270 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
273 #define StopReading() { \
275 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
278 #define StopCdda() { \
279 if (cdr.Play && !Config.Cdda) CDR_stop(); \
281 cdr.FastForward = 0; \
282 cdr.FastBackward = 0; \
285 #define SetPlaySeekRead(x, f) { \
286 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
290 #define SetResultSize(size) { \
292 cdr.ResultC = size; \
293 cdr.ResultReady = 1; \
296 static void setIrq(int log_cmd)
298 if (cdr.Stat & cdr.Reg2)
299 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
301 #ifdef CDR_LOG_CMD_IRQ
305 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
306 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
307 for (i = 0; i < cdr.ResultC; i++)
308 SysPrintf("%02x ", cdr.Result[i]);
314 // timing used in this function was taken from tests on real hardware
315 // (yes it's slow, but you probably don't want to modify it)
316 void cdrLidSeekInterrupt(void)
318 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
320 switch (cdr.DriveState) {
322 case DRIVESTATE_STANDBY:
325 SetPlaySeekRead(cdr.StatP, 0);
327 if (CDR_getStatus(&stat) == -1)
330 if (stat.Status & STATUS_SHELLOPEN)
332 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
333 cdr.DriveState = DRIVESTATE_LID_OPEN;
338 case DRIVESTATE_LID_OPEN:
339 if (CDR_getStatus(&stat) == -1)
340 stat.Status &= ~STATUS_SHELLOPEN;
343 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
344 cdr.StatP |= STATUS_SHELLOPEN;
346 // could generate error irq here, but real hardware
347 // only sometimes does that
348 // (not done when lots of commands are sent?)
350 CDRLID_INT(cdReadTime * 30);
353 else if (cdr.StatP & STATUS_ROTATING) {
354 cdr.StatP &= ~STATUS_ROTATING;
356 else if (!(stat.Status & STATUS_SHELLOPEN)) {
360 // cdr.StatP STATUS_SHELLOPEN is "sticky"
361 // and is only cleared by CdlNop
363 cdr.DriveState = DRIVESTATE_RESCAN_CD;
364 CDRLID_INT(cdReadTime * 105);
369 CDRLID_INT(cdReadTime * 3);
372 case DRIVESTATE_RESCAN_CD:
373 cdr.StatP |= STATUS_ROTATING;
374 cdr.DriveState = DRIVESTATE_PREPARE_CD;
376 // this is very long on real hardware, over 6 seconds
377 // make it a bit faster here...
378 CDRLID_INT(cdReadTime * 150);
381 case DRIVESTATE_PREPARE_CD:
382 if (cdr.StatP & STATUS_SEEK) {
383 SetPlaySeekRead(cdr.StatP, 0);
384 cdr.DriveState = DRIVESTATE_STANDBY;
387 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
388 CDRLID_INT(cdReadTime * 26);
394 static void Find_CurTrack(const u8 *time)
398 current = msf2sec(time);
400 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
401 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
402 sect = fsm2sec(cdr.ResultTD);
403 if (sect - current >= 150)
408 static void generate_subq(const u8 *time)
410 unsigned char start[3], next[3];
411 unsigned int this_s, start_s, next_s, pregap;
414 CDR_getTD(cdr.CurTrack, start);
415 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
417 CDR_getTD(cdr.CurTrack + 1, next);
420 // last track - cd size
422 next[0] = cdr.SetSectorEnd[2];
423 next[1] = cdr.SetSectorEnd[1];
424 next[2] = cdr.SetSectorEnd[0];
427 this_s = msf2sec(time);
428 start_s = fsm2sec(start);
429 next_s = fsm2sec(next);
431 cdr.TrackChanged = FALSE;
433 if (next_s - this_s < pregap) {
434 cdr.TrackChanged = TRUE;
441 relative_s = this_s - start_s;
442 if (relative_s < 0) {
444 relative_s = -relative_s;
446 sec2msf(relative_s, cdr.subq.Relative);
448 cdr.subq.Track = itob(cdr.CurTrack);
449 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
450 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
451 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
452 cdr.subq.Absolute[0] = itob(time[0]);
453 cdr.subq.Absolute[1] = itob(time[1]);
454 cdr.subq.Absolute[2] = itob(time[2]);
457 static int ReadTrack(const u8 *time)
459 unsigned char tmp[3];
462 tmp[0] = itob(time[0]);
463 tmp[1] = itob(time[1]);
464 tmp[2] = itob(time[2]);
466 if (memcmp(cdr.Prev, tmp, 3) == 0)
469 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
471 read_ok = CDR_readTrack(tmp);
473 memcpy(cdr.Prev, tmp, 3);
477 static void UpdateSubq(const u8 *time)
479 const struct SubQ *subq;
485 subq = (struct SubQ *)CDR_getBufferSub(MSF2SECT(time[0], time[1], time[2]));
486 if (subq != NULL && cdr.CurTrack == 1) {
487 crc = calcCrc((u8 *)subq + 12, 10);
488 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
489 cdr.subq.Track = subq->TrackNumber;
490 cdr.subq.Index = subq->IndexNumber;
491 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
492 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
495 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
496 time[0], time[1], time[2]);
503 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
504 cdr.subq.Track, cdr.subq.Index,
505 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
506 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
509 static void cdrPlayInterrupt_Autopause()
512 boolean abs_lev_chselect;
515 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
516 CDR_LOG( "CDDA STOP\n" );
518 // Magic the Gathering
519 // - looping territory cdda
522 //cdr.ResultReady = 1;
523 //cdr.Stat = DataReady;
525 setIrq(0x1000); // 0x1000 just for logging purposes
528 SetPlaySeekRead(cdr.StatP, 0);
530 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
531 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
533 cdr.Result[0] = cdr.StatP;
534 cdr.Result[1] = cdr.subq.Track;
535 cdr.Result[2] = cdr.subq.Index;
537 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
539 /* 8 is a hack. For accuracy, it should be 588. */
540 for (i = 0; i < 8; i++)
542 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
544 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
545 abs_lev_max |= abs_lev_chselect << 15;
547 if (cdr.subq.Absolute[2] & 0x10) {
548 cdr.Result[3] = cdr.subq.Relative[0];
549 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
550 cdr.Result[5] = cdr.subq.Relative[2];
553 cdr.Result[3] = cdr.subq.Absolute[0];
554 cdr.Result[4] = cdr.subq.Absolute[1];
555 cdr.Result[5] = cdr.subq.Absolute[2];
558 cdr.Result[6] = abs_lev_max >> 0;
559 cdr.Result[7] = abs_lev_max >> 8;
561 // Rayman: Logo freeze (resultready + dataready)
563 cdr.Stat = DataReady;
574 static int cdrSeekTime(unsigned char *target)
576 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
577 int pausePenalty, seekTime = abs(diff) * (cdReadTime / 2000);
578 seekTime = MAX_VALUE(seekTime, 20000);
580 // need this stupidly long penalty or else Spyro2 intro desyncs
581 pausePenalty = (s32)(psxRegs.cycle - cdr.LastReadCycles) > cdReadTime * 8 ? cdReadTime * 25 : 0;
582 seekTime += pausePenalty;
584 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
585 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
589 static u32 cdrAlignTimingHack(u32 cycles)
592 * timing hack for T'ai Fu - Wrath of the Tiger:
593 * The game has a bug where it issues some cdc commands from a low priority
594 * vint handler, however there is a higher priority default bios handler
595 * that acks the vint irq and returns, so game's handler is not reached
596 * (see bios irq handler chains at e004 and the game's irq handling func
597 * at 80036810). For the game to work, vint has to arrive after the bios
598 * vint handler rejects some other irq (of which only cd and rcnt2 are
599 * active), but before the game's handler loop reads I_STAT. The time
600 * window for this is quite small (~1k cycles of so). Apparently this
601 * somehow happens naturally on the real hardware.
603 * Note: always enforcing this breaks other games like Crash PAL version
604 * (inputs get dropped because bios handler doesn't see interrupts).
607 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
609 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
610 vint_rel += PSXCLK / 60;
611 while ((s32)(vint_rel - cycles) < 0)
612 vint_rel += PSXCLK / 60;
616 static void cdrUpdateTransferBuf(const u8 *buf);
617 static void cdrReadInterrupt(void);
618 static void cdrPrepCdda(s16 *buf, int samples);
619 static void cdrAttenuate(s16 *buf, int samples, int stereo);
621 static void msfiAdd(u8 *msfi, u32 count)
635 static void msfiSub(u8 *msfi, u32 count)
639 if ((s8)msfi[2] < 0) {
642 if ((s8)msfi[1] < 0) {
649 void cdrPlayReadInterrupt(void)
651 cdr.LastReadCycles = psxRegs.cycle;
658 if (!cdr.Play) return;
660 CDR_LOG( "CDDA - %d:%d:%d\n",
661 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
663 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
664 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
666 SetPlaySeekRead(cdr.StatP, 0);
667 cdr.TrackChanged = TRUE;
670 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
673 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
674 cdrPlayInterrupt_Autopause();
676 if (!cdr.Muted && !Config.Cdda) {
677 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
678 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
679 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
683 msfiAdd(cdr.SetSectorPlay, 1);
685 // update for CdlGetlocP/autopause
686 generate_subq(cdr.SetSectorPlay);
688 CDRPLAYREAD_INT(cdReadTime, 0);
691 #define CMD_PART2 0x100
692 #define CMD_WHILE_NOT_READY 0x200
694 void cdrInterrupt(void) {
695 int start_rotating = 0;
697 u32 cycles, seekTime = 0;
698 u32 second_resp_time = 0;
708 CDR_LOG_I("cmd %02x with irqstat %x\n",
709 cdr.CmdInProgress, cdr.Stat);
712 if (cdr.Irq1Pending) {
713 // hand out the "newest" sector, according to nocash
714 cdrUpdateTransferBuf(CDR_getBuffer());
715 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
716 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
717 cdr.CmdInProgress, cdr.Irq1Pending);
719 cdr.Result[0] = cdr.Irq1Pending;
720 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
728 cdr.Result[0] = cdr.StatP;
729 cdr.Stat = Acknowledge;
731 Cmd = cdr.CmdInProgress;
732 cdr.CmdInProgress = 0;
741 switch (cdr.DriveState) {
742 case DRIVESTATE_PREPARE_CD:
744 // Syphon filter 2 expects commands to work shortly after it sees
745 // STATUS_ROTATING, so give up trying to emulate the startup seq
746 cdr.DriveState = DRIVESTATE_STANDBY;
747 cdr.StatP &= ~STATUS_SEEK;
748 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
752 case DRIVESTATE_LID_OPEN:
753 case DRIVESTATE_RESCAN_CD:
754 // no disk or busy with the initial scan, allowed cmds are limited
755 not_ready = CMD_WHILE_NOT_READY;
759 switch (Cmd | not_ready) {
761 case CdlNop + CMD_WHILE_NOT_READY:
762 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
763 cdr.StatP &= ~STATUS_SHELLOPEN;
767 case CdlSetloc + CMD_WHILE_NOT_READY:
768 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
770 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
771 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))
773 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
774 if (++cdr.errorRetryhack > 100)
776 error = ERROR_INVALIDARG;
781 for (i = 0; i < 3; i++)
782 set_loc[i] = btoi(cdr.Param[i]);
783 memcpy(cdr.SetSector, set_loc, 3);
784 cdr.SetSector[3] = 0;
785 cdr.SetlocPending = 1;
786 cdr.errorRetryhack = 0;
795 cdr.FastBackward = 0;
799 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
801 if (ParamC != 0 && cdr.Param[0] != 0) {
802 int track = btoi( cdr.Param[0] );
804 if (track <= cdr.ResultTN[1])
805 cdr.CurTrack = track;
807 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
809 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
810 for (i = 0; i < 3; i++)
811 set_loc[i] = cdr.ResultTD[2 - i];
812 seekTime = cdrSeekTime(set_loc);
813 memcpy(cdr.SetSectorPlay, set_loc, 3);
816 else if (cdr.SetlocPending) {
817 seekTime = cdrSeekTime(cdr.SetSector);
818 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
821 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
822 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
824 cdr.SetlocPending = 0;
827 Rayman: detect track changes
830 Twisted Metal 2: skip PREGAP + starting accurate SubQ
831 - plays tracks without retry play
833 Wild 9: skip PREGAP + starting accurate SubQ
834 - plays tracks without retry play
836 Find_CurTrack(cdr.SetSectorPlay);
837 generate_subq(cdr.SetSectorPlay);
838 cdr.LocL[0] = LOCL_INVALID;
839 cdr.SubqForwardSectors = 1;
840 cdr.TrackChanged = FALSE;
842 cdr.ReportDelay = 60;
846 CDR_play(cdr.SetSectorPlay);
848 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
850 // BIOS player - set flag again
853 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
858 // TODO: error 80 if stopped
861 // GameShark CD Player: Calls 2x + Play 2x
863 cdr.FastBackward = 0;
869 // GameShark CD Player: Calls 2x + Play 2x
870 cdr.FastBackward = 1;
875 if (cdr.DriveState != DRIVESTATE_STOPPED) {
876 error = ERROR_INVALIDARG;
879 second_resp_time = cdReadTime * 125 / 2;
883 case CdlStandby + CMD_PART2:
889 // grab time for current track
890 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
892 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
893 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
894 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
899 SetPlaySeekRead(cdr.StatP, 0);
900 cdr.StatP &= ~STATUS_ROTATING;
901 cdr.LocL[0] = LOCL_INVALID;
903 second_resp_time = 0x800;
904 if (cdr.DriveState == DRIVESTATE_STANDBY)
905 second_resp_time = cdReadTime * 30 / 2;
907 cdr.DriveState = DRIVESTATE_STOPPED;
910 case CdlStop + CMD_PART2:
918 // how the drive maintains the position while paused is quite
919 // complicated, this is the minimum to make "Bedlam" happy
920 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
924 Gundam Battle Assault 2: much slower (*)
925 - Fixes boot, gameplay
927 Hokuto no Ken 2: slower
928 - Fixes intro + subtitles
930 InuYasha - Feudal Fairy Tale: slower
933 /* Gameblabla - Tightening the timings (as taken from Duckstation).
934 * The timings from Duckstation are based upon hardware tests.
935 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
936 * seems to be timing sensitive as it can depend on the CPU's clock speed.
938 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
940 second_resp_time = 7000;
944 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
946 SetPlaySeekRead(cdr.StatP, 0);
949 case CdlPause + CMD_PART2:
954 case CdlReset + CMD_WHILE_NOT_READY:
957 SetPlaySeekRead(cdr.StatP, 0);
958 cdr.LocL[0] = LOCL_INVALID;
960 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
961 second_resp_time = not_ready ? 70000 : 4100000;
965 case CdlReset + CMD_PART2:
966 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
979 cdr.File = cdr.Param[0];
980 cdr.Channel = cdr.Param[1];
984 case CdlSetmode + CMD_WHILE_NOT_READY:
985 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
986 cdr.Mode = cdr.Param[0];
990 case CdlGetparam + CMD_WHILE_NOT_READY:
991 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
993 cdr.Result[1] = cdr.Mode;
995 cdr.Result[3] = cdr.File;
996 cdr.Result[4] = cdr.Channel;
1000 if (cdr.LocL[0] == LOCL_INVALID) {
1005 memcpy(cdr.Result, cdr.LocL, 8);
1010 memcpy(&cdr.Result, &cdr.subq, 8);
1013 case CdlReadT: // SetSession?
1015 second_resp_time = cdReadTime * 290 / 4;
1019 case CdlReadT + CMD_PART2:
1020 cdr.Stat = Complete;
1025 if (CDR_getTN(cdr.ResultTN) == -1) {
1026 cdr.Stat = DiskError;
1027 cdr.Result[0] |= STATUS_ERROR;
1029 cdr.Stat = Acknowledge;
1030 cdr.Result[1] = itob(cdr.ResultTN[0]);
1031 cdr.Result[2] = itob(cdr.ResultTN[1]);
1036 cdr.Track = btoi(cdr.Param[0]);
1038 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1039 cdr.Stat = DiskError;
1040 cdr.Result[0] |= STATUS_ERROR;
1042 cdr.Stat = Acknowledge;
1043 cdr.Result[0] = cdr.StatP;
1044 cdr.Result[1] = itob(cdr.ResultTD[2]);
1045 cdr.Result[2] = itob(cdr.ResultTD[1]);
1046 /* According to Nocash's documentation, the function doesn't care about ff.
1047 * This can be seen also in Mednafen's implementation. */
1048 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1056 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1058 seekTime = cdrSeekTime(cdr.SetSector);
1059 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1061 Crusaders of Might and Magic = 0.5x-4x
1062 - fix cutscene speech start
1064 Eggs of Steel = 2x-?
1068 - fix cutscene speech
1073 second_resp_time = cdReadTime + seekTime;
1077 case CdlSeekL + CMD_PART2:
1078 case CdlSeekP + CMD_PART2:
1079 SetPlaySeekRead(cdr.StatP, 0);
1080 cdr.Result[0] = cdr.StatP;
1081 cdr.Stat = Complete;
1083 Find_CurTrack(cdr.SetSectorPlay);
1084 read_ok = ReadTrack(cdr.SetSectorPlay);
1085 if (read_ok && (buf = CDR_getBuffer()))
1086 memcpy(cdr.LocL, buf, 8);
1087 UpdateSubq(cdr.SetSectorPlay);
1088 cdr.TrackChanged = FALSE;
1092 case CdlTest + CMD_WHILE_NOT_READY:
1093 switch (cdr.Param[0]) {
1094 case 0x20: // System Controller ROM Version
1096 memcpy(cdr.Result, Test20, 4);
1100 memcpy(cdr.Result, Test22, 4);
1102 case 0x23: case 0x24:
1104 memcpy(cdr.Result, Test23, 4);
1110 second_resp_time = 20480;
1113 case CdlID + CMD_PART2:
1115 cdr.Result[0] = cdr.StatP;
1120 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1121 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1122 cdr.Result[1] = 0xc0;
1126 cdr.Result[1] |= 0x10;
1127 if (CdromId[0] == '\0')
1128 cdr.Result[1] |= 0x80;
1130 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1132 /* This adds the string "PCSX" in Playstation bios boot screen */
1133 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1134 cdr.Stat = Complete;
1138 case CdlInit + CMD_WHILE_NOT_READY:
1141 SetPlaySeekRead(cdr.StatP, 0);
1142 // yes, it really sets STATUS_SHELLOPEN
1143 cdr.StatP |= STATUS_SHELLOPEN;
1144 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1150 case CdlGetQ + CMD_WHILE_NOT_READY:
1154 case CdlReadToc + CMD_WHILE_NOT_READY:
1155 cdr.LocL[0] = LOCL_INVALID;
1156 second_resp_time = cdReadTime * 180 / 4;
1160 case CdlReadToc + CMD_PART2:
1161 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1162 cdr.Stat = Complete;
1167 if (cdr.Reading && !cdr.SetlocPending)
1170 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1172 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1173 // Read* acts as play for cdda tracks in cdda mode
1177 if (cdr.SetlocPending) {
1178 seekTime = cdrSeekTime(cdr.SetSector);
1179 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1180 cdr.SetlocPending = 0;
1183 cdr.FirstSector = 1;
1185 // Fighting Force 2 - update subq time immediately
1187 UpdateSubq(cdr.SetSectorPlay);
1188 cdr.LocL[0] = LOCL_INVALID;
1189 cdr.SubqForwardSectors = 1;
1190 cdr.sectorsRead = 0;
1192 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1194 if (Config.hacks.cdr_read_timing)
1195 cycles = cdrAlignTimingHack(cycles);
1196 CDRPLAYREAD_INT(cycles, 1);
1198 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1204 error = ERROR_INVALIDCMD;
1208 CDR_LOG_I("cmd %02x error %02x\n", Cmd, error);
1210 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1211 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1212 cdr.Stat = DiskError;
1216 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1217 cdr.DriveState = DRIVESTATE_STANDBY;
1218 cdr.StatP |= STATUS_ROTATING;
1221 if (second_resp_time) {
1222 cdr.CmdInProgress = Cmd | 0x100;
1223 CDR_INT(second_resp_time);
1225 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1226 cdr.CmdInProgress = cdr.Cmd;
1227 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1234 #define ssat32_to_16(v) \
1235 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1237 #define ssat32_to_16(v) do { \
1238 if (v < -32768) v = -32768; \
1239 else if (v > 32767) v = 32767; \
1243 static void cdrPrepCdda(s16 *buf, int samples)
1245 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1247 for (i = 0; i < samples; i++) {
1248 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1249 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1254 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1257 int ll = cdr.AttenuatorLeftToLeft;
1258 int lr = cdr.AttenuatorLeftToRight;
1259 int rl = cdr.AttenuatorRightToLeft;
1260 int rr = cdr.AttenuatorRightToRight;
1262 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1265 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1269 for (i = 0; i < samples; i++) {
1272 l = (l * ll + r * rl) >> 7;
1273 r = (r * rr + l * lr) >> 7;
1281 for (i = 0; i < samples; i++) {
1283 l = l * (ll + rl) >> 7;
1284 //r = r * (rr + lr) >> 7;
1292 static void cdrReadInterruptSetResult(unsigned char result)
1295 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1296 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1297 cdr.CmdInProgress, cdr.Stat);
1298 cdr.Irq1Pending = result;
1302 cdr.Result[0] = result;
1303 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1307 static void cdrUpdateTransferBuf(const u8 *buf)
1311 memcpy(cdr.Transfer, buf, DATA_SIZE);
1312 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1313 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1314 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1315 if (cdr.FifoOffset < 2048 + 12)
1316 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1319 static void cdrReadInterrupt(void)
1321 u8 *buf = NULL, *hdr;
1325 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1326 msfiAdd(subqPos, cdr.SubqForwardSectors);
1327 UpdateSubq(subqPos);
1328 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1329 cdr.SubqForwardSectors++;
1330 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1334 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1335 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1338 read_ok = ReadTrack(cdr.SetSectorPlay);
1340 buf = CDR_getBuffer();
1345 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1346 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1349 memcpy(cdr.LocL, buf, 8);
1351 if (!cdr.Stat && !cdr.Irq1Pending)
1352 cdrUpdateTransferBuf(buf);
1354 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1356 // Firemen 2: Multi-XA files - briefings, cutscenes
1357 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1359 cdr.Channel = hdr[1];
1363 * Skips playing on channel 255.
1364 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1365 * TODO : Check if this is the proper behaviour.
1367 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1368 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1370 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1371 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1372 cdr.FirstSector = 0;
1374 else cdr.FirstSector = -1;
1379 Croc 2: $40 - only FORM1 (*)
1380 Judge Dredd: $C8 - only FORM1 (*)
1381 Sim Theme Park - no adpcm at all (zero)
1384 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1385 cdrReadInterruptSetResult(cdr.StatP);
1387 msfiAdd(cdr.SetSectorPlay, 1);
1389 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1398 bit 5 - 1 result ready
1400 bit 7 - 1 command being processed
1403 unsigned char cdrRead0(void) {
1404 if (cdr.ResultReady)
1409 cdr.Ctrl |= 0x40; // data fifo not empty
1411 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1414 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1416 return psxHu8(0x1800) = cdr.Ctrl;
1419 void cdrWrite0(unsigned char rt) {
1420 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1422 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1425 unsigned char cdrRead1(void) {
1426 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1427 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1431 if (cdr.ResultP == cdr.ResultC)
1432 cdr.ResultReady = 0;
1434 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1436 return psxHu8(0x1801);
1439 void cdrWrite1(unsigned char rt) {
1440 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1441 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1443 switch (cdr.Ctrl & 3) {
1447 cdr.AttenuatorRightToRightT = rt;
1453 #ifdef CDR_LOG_CMD_IRQ
1454 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1457 SysPrintf(" Param[%d] = {", cdr.ParamC);
1458 for (i = 0; i < cdr.ParamC; i++)
1459 SysPrintf(" %x,", cdr.Param[i]);
1466 cdr.ResultReady = 0;
1469 if (!cdr.CmdInProgress) {
1470 cdr.CmdInProgress = rt;
1471 // should be something like 12k + controller delays
1475 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1476 rt, cdr.Cmd, cdr.CmdInProgress);
1477 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1478 cdr.CmdInProgress = rt;
1484 unsigned char cdrRead2(void) {
1485 unsigned char ret = cdr.Transfer[0x920];
1487 if (cdr.FifoOffset < cdr.FifoSize)
1488 ret = cdr.Transfer[cdr.FifoOffset++];
1490 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1492 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1496 void cdrWrite2(unsigned char rt) {
1497 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1498 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1500 switch (cdr.Ctrl & 3) {
1502 if (cdr.ParamC < 8) // FIXME: size and wrapping
1503 cdr.Param[cdr.ParamC++] = rt;
1510 cdr.AttenuatorLeftToLeftT = rt;
1513 cdr.AttenuatorRightToLeftT = rt;
1518 unsigned char cdrRead3(void) {
1520 psxHu8(0x1803) = cdr.Stat | 0xE0;
1522 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1524 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1525 return psxHu8(0x1803);
1528 void cdrWrite3(unsigned char rt) {
1529 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1530 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1532 switch (cdr.Ctrl & 3) {
1536 if (cdr.Stat & rt) {
1537 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1538 + psxRegs.intCycle[PSXINT_CDR].cycle;
1539 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1540 #ifdef CDR_LOG_CMD_IRQ
1541 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1542 !!pending, cdr.CmdInProgress,
1543 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1545 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1546 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1549 if (cdr.CmdInProgress) {
1550 c = 2048 - (psxRegs.cycle - nextCycle);
1551 c = MAX_VALUE(c, 512);
1562 cdr.AttenuatorLeftToRightT = rt;
1566 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1567 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1568 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1569 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1575 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1576 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1578 else if (rt & 0x80) {
1579 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1580 case MODE_SIZE_2328:
1582 cdr.FifoOffset = 12;
1583 cdr.FifoSize = 2048 + 12;
1586 case MODE_SIZE_2340:
1589 cdr.FifoSize = 2340;
1593 else if (!(rt & 0xc0))
1594 cdr.FifoOffset = DATA_SIZE; // fifo empty
1597 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1598 u32 cdsize, max_words;
1603 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1604 if (cdr.FifoOffset == 0) {
1606 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1611 switch (chcr & 0x71000000) {
1613 ptr = getDmaRam(madr, &max_words);
1614 if (ptr == INVALID_PTR) {
1615 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1619 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1622 GS CDX: Enhancement CD crash
1625 - Spams DMA3 and gets buffer overrun
1627 size = DATA_SIZE - cdr.FifoOffset;
1630 if (size > max_words * 4)
1631 size = max_words * 4;
1634 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1635 cdr.FifoOffset += size;
1637 if (size < cdsize) {
1638 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1639 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1641 psxCpu->Clear(madr, cdsize / 4);
1643 CDRDMA_INT((cdsize/4) * 24);
1645 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1647 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1648 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1652 psxRegs.cycle += (cdsize/4) * 24 - 20;
1657 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1661 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1665 void cdrDmaInterrupt(void)
1667 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1669 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1674 static void getCdInfo(void)
1678 CDR_getTN(cdr.ResultTN);
1679 CDR_getTD(0, cdr.SetSectorEnd);
1680 tmp = cdr.SetSectorEnd[0];
1681 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1682 cdr.SetSectorEnd[2] = tmp;
1686 memset(&cdr, 0, sizeof(cdr));
1692 cdr.FifoOffset = DATA_SIZE; // fifo empty
1693 if (CdromId[0] == '\0') {
1694 cdr.DriveState = DRIVESTATE_STOPPED;
1698 cdr.DriveState = DRIVESTATE_STANDBY;
1699 cdr.StatP = STATUS_ROTATING;
1702 // BIOS player - default values
1703 cdr.AttenuatorLeftToLeft = 0x80;
1704 cdr.AttenuatorLeftToRight = 0x00;
1705 cdr.AttenuatorRightToLeft = 0x00;
1706 cdr.AttenuatorRightToRight = 0x80;
1711 int cdrFreeze(void *f, int Mode) {
1715 if (Mode == 0 && !Config.Cdda)
1718 cdr.freeze_ver = 0x63647202;
1719 gzfreeze(&cdr, sizeof(cdr));
1722 cdr.ParamP = cdr.ParamC;
1723 tmp = cdr.FifoOffset;
1726 gzfreeze(&tmp, sizeof(tmp));
1731 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1732 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1733 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1734 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1736 // read right sub data
1737 tmpp[0] = btoi(cdr.Prev[0]);
1738 tmpp[1] = btoi(cdr.Prev[1]);
1739 tmpp[2] = btoi(cdr.Prev[2]);
1744 if (cdr.freeze_ver < 0x63647202)
1745 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1747 Find_CurTrack(cdr.SetSectorPlay);
1749 CDR_play(cdr.SetSectorPlay);
1752 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1753 // old versions did not latch Reg2, have to fixup..
1754 if (cdr.Reg2 == 0) {
1755 SysPrintf("cdrom: fixing up old savestate\n");
1758 // also did not save Attenuator..
1759 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1760 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1762 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1770 void LidInterrupt(void) {
1772 cdrLidSeekInterrupt();