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;
55 unsigned char IrqMask;
56 unsigned char unused2;
58 unsigned char IrqStat;
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];
98 unsigned char FileChannelSelected;
99 unsigned char CurFile, CurChannel;
100 int FilterFile, FilterChannel;
101 unsigned char LocL[8];
112 u32 LastReadSeekCycles;
116 u8 DriveState; // enum drive_state
121 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
122 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
123 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
124 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
126 static s16 read_buf[CD_FRAMESIZE_RAW/2];
128 /* CD-ROM magic numbers */
129 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
134 #define CdlBackward 5
142 #define CdlSetfilter 13
143 #define CdlSetmode 14
144 #define CdlGetparam 15
145 #define CdlGetlocL 16
146 #define CdlGetlocP 17
152 #define CdlSetclock 23
153 #define CdlGetclock 24
159 #define CdlReadToc 30
161 #ifdef CDR_LOG_CMD_IRQ
162 static const char * const CmdName[0x100] = {
163 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
164 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
165 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
166 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
167 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
168 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
169 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
170 "CdlInit", NULL, "CDlReadToc", NULL
174 unsigned char Test04[] = { 0 };
175 unsigned char Test05[] = { 0 };
176 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
177 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
178 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
184 #define Acknowledge 3
189 #define MODE_SPEED (1<<7) // 0x80
190 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
191 #define MODE_SIZE_2340 (1<<5) // 0x20
192 #define MODE_SIZE_2328 (1<<4) // 0x10
193 #define MODE_SIZE_2048 (0<<4) // 0x00
194 #define MODE_SF (1<<3) // 0x08 channel on/off
195 #define MODE_REPORT (1<<2) // 0x04
196 #define MODE_AUTOPAUSE (1<<1) // 0x02
197 #define MODE_CDDA (1<<0) // 0x01
200 #define STATUS_PLAY (1<<7) // 0x80
201 #define STATUS_SEEK (1<<6) // 0x40
202 #define STATUS_READ (1<<5) // 0x20
203 #define STATUS_SHELLOPEN (1<<4) // 0x10
204 #define STATUS_UNKNOWN3 (1<<3) // 0x08
205 #define STATUS_SEEKERROR (1<<2) // 0x04
206 #define STATUS_ROTATING (1<<1) // 0x02
207 #define STATUS_ERROR (1<<0) // 0x01
210 #define ERROR_NOTREADY (1<<7) // 0x80
211 #define ERROR_INVALIDCMD (1<<6) // 0x40
212 #define ERROR_BAD_ARGNUM (1<<5) // 0x20
213 #define ERROR_BAD_ARGVAL (1<<4) // 0x10
214 #define ERROR_SHELLOPEN (1<<3) // 0x08
216 // 1x = 75 sectors per second
217 // PSXCLK = 1 sec in the ps
218 // so (PSXCLK / 75) = cdr read time (linuzappz)
219 #define cdReadTime (PSXCLK / 75)
221 #define LOCL_INVALID 0xff
222 #define SUBQ_FORWARD_SECTORS 2u
225 DRIVESTATE_STANDBY = 0, // different from paused
227 DRIVESTATE_RESCAN_CD,
228 DRIVESTATE_PREPARE_CD,
231 DRIVESTATE_PLAY_READ,
235 static struct CdrStat stat;
237 static unsigned int msf2sec(const u8 *msf) {
238 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
241 // for that weird psemu API..
242 static unsigned int fsm2sec(const u8 *msf) {
243 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
246 static void sec2msf(unsigned int s, u8 *msf) {
247 msf[0] = s / 75 / 60;
248 s = s - msf[0] * 75 * 60;
254 // cdrPlayReadInterrupt
255 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
257 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
259 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
261 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
262 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
263 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
266 #define StopReading() { \
268 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
271 #define StopCdda() { \
272 if (cdr.Play && !Config.Cdda) CDR_stop(); \
274 cdr.FastForward = 0; \
275 cdr.FastBackward = 0; \
278 #define SetPlaySeekRead(x, f) { \
279 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
283 #define SetResultSize_(size) { \
285 cdr.ResultC = size; \
286 cdr.ResultReady = 1; \
289 #define SetResultSize(size) { \
290 if (cdr.ResultP < cdr.ResultC) \
291 CDR_LOG_I("overwriting result, len=%u\n", cdr.ResultC); \
292 SetResultSize_(size); \
295 static void setIrq(u8 irq, int log_cmd)
297 u8 old = cdr.IrqStat & cdr.IrqMask ? 1 : 0;
298 u8 new_ = irq & cdr.IrqMask ? 1 : 0;
301 if ((old ^ new_) & new_)
302 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
304 #ifdef CDR_LOG_CMD_IRQ
308 CDR_LOG_I("CDR IRQ=%d cmd %02x irqstat %02x: ",
309 !!(cdr.IrqStat & cdr.IrqMask), log_cmd, cdr.IrqStat);
310 for (i = 0; i < cdr.ResultC; i++)
311 SysPrintf("%02x ", cdr.Result[i]);
317 // timing used in this function was taken from tests on real hardware
318 // (yes it's slow, but you probably don't want to modify it)
319 void cdrLidSeekInterrupt(void)
321 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
323 switch (cdr.DriveState) {
325 case DRIVESTATE_STANDBY:
328 SetPlaySeekRead(cdr.StatP, 0);
330 if (CDR_getStatus(&stat) == -1)
333 if (stat.Status & STATUS_SHELLOPEN)
335 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
336 cdr.DriveState = DRIVESTATE_LID_OPEN;
337 set_event(PSXINT_CDRLID, 0x800);
341 case DRIVESTATE_LID_OPEN:
342 if (CDR_getStatus(&stat) == -1)
343 stat.Status &= ~STATUS_SHELLOPEN;
346 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
348 SetPlaySeekRead(cdr.StatP, 0);
349 cdr.StatP |= STATUS_SHELLOPEN;
351 // IIRC this sometimes doesn't happen on real hw
352 // (when lots of commands are sent?)
354 cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
355 cdr.Result[1] = ERROR_SHELLOPEN;
356 if (cdr.CmdInProgress) {
357 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
358 cdr.CmdInProgress = 0;
359 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
360 cdr.Result[1] = ERROR_NOTREADY;
362 setIrq(DiskError, 0x1006);
364 set_event(PSXINT_CDRLID, cdReadTime * 30);
367 else if (cdr.StatP & STATUS_ROTATING) {
368 cdr.StatP &= ~STATUS_ROTATING;
370 else if (!(stat.Status & STATUS_SHELLOPEN)) {
374 // cdr.StatP STATUS_SHELLOPEN is "sticky"
375 // and is only cleared by CdlNop
377 cdr.DriveState = DRIVESTATE_RESCAN_CD;
378 set_event(PSXINT_CDRLID, cdReadTime * 105);
383 set_event(PSXINT_CDRLID, cdReadTime * 3);
386 case DRIVESTATE_RESCAN_CD:
387 cdr.StatP |= STATUS_ROTATING;
388 cdr.DriveState = DRIVESTATE_PREPARE_CD;
390 // this is very long on real hardware, over 6 seconds
391 // make it a bit faster here...
392 set_event(PSXINT_CDRLID, cdReadTime * 150);
395 case DRIVESTATE_PREPARE_CD:
396 if (cdr.StatP & STATUS_SEEK) {
397 SetPlaySeekRead(cdr.StatP, 0);
398 cdr.DriveState = DRIVESTATE_STANDBY;
401 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
402 set_event(PSXINT_CDRLID, cdReadTime * 26);
408 static void Find_CurTrack(const u8 *time)
412 current = msf2sec(time);
414 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
415 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
416 sect = fsm2sec(cdr.ResultTD);
417 if (sect - current >= 150)
422 static void generate_subq(const u8 *time)
424 unsigned char start[3], next[3];
425 unsigned int this_s, start_s, next_s, pregap;
428 CDR_getTD(cdr.CurTrack, start);
429 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
431 CDR_getTD(cdr.CurTrack + 1, next);
434 // last track - cd size
436 next[0] = cdr.SetSectorEnd[2];
437 next[1] = cdr.SetSectorEnd[1];
438 next[2] = cdr.SetSectorEnd[0];
441 this_s = msf2sec(time);
442 start_s = fsm2sec(start);
443 next_s = fsm2sec(next);
445 cdr.TrackChanged = FALSE;
447 if (next_s - this_s < pregap) {
448 cdr.TrackChanged = TRUE;
455 relative_s = this_s - start_s;
456 if (relative_s < 0) {
458 relative_s = -relative_s;
460 sec2msf(relative_s, cdr.subq.Relative);
462 cdr.subq.Track = itob(cdr.CurTrack);
463 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
464 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
465 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
466 cdr.subq.Absolute[0] = itob(time[0]);
467 cdr.subq.Absolute[1] = itob(time[1]);
468 cdr.subq.Absolute[2] = itob(time[2]);
471 static int ReadTrack(const u8 *time)
473 unsigned char tmp[3];
476 tmp[0] = itob(time[0]);
477 tmp[1] = itob(time[1]);
478 tmp[2] = itob(time[2]);
480 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
482 if (memcmp(cdr.Prev, tmp, 3) == 0)
485 read_ok = CDR_readTrack(tmp);
487 memcpy(cdr.Prev, tmp, 3);
491 static void UpdateSubq(const u8 *time)
493 const struct SubQ *subq;
494 int s = MSF2SECT(time[0], time[1], time[2]);
500 subq = (struct SubQ *)CDR_getBufferSub(s);
501 if (subq != NULL && cdr.CurTrack == 1) {
502 crc = calcCrc((u8 *)subq + 12, 10);
503 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
504 cdr.subq.Track = subq->TrackNumber;
505 cdr.subq.Index = subq->IndexNumber;
506 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
507 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
510 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
511 time[0], time[1], time[2]);
518 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
519 cdr.subq.Track, cdr.subq.Index,
520 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
521 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
524 static void cdrPlayInterrupt_Autopause()
527 boolean abs_lev_chselect;
530 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
531 CDR_LOG_I("autopause\n");
534 cdr.Result[0] = cdr.StatP;
535 setIrq(DataEnd, 0x1000); // 0x1000 just for logging purposes
538 SetPlaySeekRead(cdr.StatP, 0);
539 cdr.DriveState = DRIVESTATE_PAUSED;
541 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
542 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
545 cdr.Result[0] = cdr.StatP;
546 cdr.Result[1] = cdr.subq.Track;
547 cdr.Result[2] = cdr.subq.Index;
549 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
551 /* 8 is a hack. For accuracy, it should be 588. */
552 for (i = 0; i < 8; i++)
554 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
556 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
557 abs_lev_max |= abs_lev_chselect << 15;
559 if (cdr.subq.Absolute[2] & 0x10) {
560 cdr.Result[3] = cdr.subq.Relative[0];
561 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
562 cdr.Result[5] = cdr.subq.Relative[2];
565 cdr.Result[3] = cdr.subq.Absolute[0];
566 cdr.Result[4] = cdr.subq.Absolute[1];
567 cdr.Result[5] = cdr.subq.Absolute[2];
569 cdr.Result[6] = abs_lev_max >> 0;
570 cdr.Result[7] = abs_lev_max >> 8;
572 setIrq(DataReady, 0x1001);
579 static int cdrSeekTime(unsigned char *target)
581 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
582 int seekTime = abs(diff) * (cdReadTime / 2000);
583 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
584 seekTime = MAX_VALUE(seekTime, 20000);
586 // need this stupidly long penalty or else Spyro2 intro desyncs
587 // note: if misapplied this breaks MGS cutscenes among other things
588 if (cdr.DriveState == DRIVESTATE_PAUSED && cyclesSinceRS > cdReadTime * 50)
589 seekTime += cdReadTime * 25;
590 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
591 // and then wants some slack time
592 else if (cdr.DriveState == DRIVESTATE_PAUSED || cyclesSinceRS < cdReadTime *3/2)
593 seekTime += cdReadTime;
595 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
596 CDR_LOG("seek: %.2f %.2f (%.2f) st %d\n", (float)seekTime / PSXCLK,
597 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime,
602 static u32 cdrAlignTimingHack(u32 cycles)
605 * timing hack for T'ai Fu - Wrath of the Tiger:
606 * The game has a bug where it issues some cdc commands from a low priority
607 * vint handler, however there is a higher priority default bios handler
608 * that acks the vint irq and returns, so game's handler is not reached
609 * (see bios irq handler chains at e004 and the game's irq handling func
610 * at 80036810). For the game to work, vint has to arrive after the bios
611 * vint handler rejects some other irq (of which only cd and rcnt2 are
612 * active), but before the game's handler loop reads I_STAT. The time
613 * window for this is quite small (~1k cycles of so). Apparently this
614 * somehow happens naturally on the real hardware.
616 * Note: always enforcing this breaks other games like Crash PAL version
617 * (inputs get dropped because bios handler doesn't see interrupts).
620 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
622 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
623 vint_rel += PSXCLK / 60;
624 while ((s32)(vint_rel - cycles) < 0)
625 vint_rel += PSXCLK / 60;
629 static void cdrUpdateTransferBuf(const u8 *buf);
630 static void cdrReadInterrupt(void);
631 static void cdrPrepCdda(s16 *buf, int samples);
633 static void msfiAdd(u8 *msfi, u32 count)
647 static void msfiSub(u8 *msfi, u32 count)
651 if ((s8)msfi[2] < 0) {
654 if ((s8)msfi[1] < 0) {
661 void cdrPlayReadInterrupt(void)
663 cdr.LastReadSeekCycles = psxRegs.cycle;
670 if (!cdr.Play) return;
672 CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
673 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
675 cdr.DriveState = DRIVESTATE_PLAY_READ;
676 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
677 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
678 CDR_LOG_I("end stop\n");
680 SetPlaySeekRead(cdr.StatP, 0);
681 cdr.TrackChanged = TRUE;
682 cdr.DriveState = DRIVESTATE_PAUSED;
685 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
688 if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
689 cdrPlayInterrupt_Autopause();
691 if (cdr.Play && !Config.Cdda) {
692 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
693 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
696 msfiAdd(cdr.SetSectorPlay, 1);
698 // update for CdlGetlocP/autopause
699 generate_subq(cdr.SetSectorPlay);
701 CDRPLAYREAD_INT(cdReadTime, 0);
704 #define CMD_PART2 0x100
705 #define CMD_WHILE_NOT_READY 0x200
707 void cdrInterrupt(void) {
708 int start_rotating = 0;
710 u32 cycles, seekTime = 0;
711 u32 second_resp_time = 0;
717 u8 IrqStat = Acknowledge;
722 CDR_LOG_I("cmd %02x with irqstat %x\n",
723 cdr.CmdInProgress, cdr.IrqStat);
726 if (cdr.Irq1Pending) {
727 // hand out the "newest" sector, according to nocash
728 cdrUpdateTransferBuf(CDR_getBuffer());
729 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
730 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
731 cdr.CmdInProgress, cdr.Irq1Pending);
733 cdr.Result[0] = cdr.Irq1Pending;
735 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
741 cdr.Result[0] = cdr.StatP;
743 Cmd = cdr.CmdInProgress;
744 cdr.CmdInProgress = 0;
753 switch (cdr.DriveState) {
754 case DRIVESTATE_PREPARE_CD:
756 // Syphon filter 2 expects commands to work shortly after it sees
757 // STATUS_ROTATING, so give up trying to emulate the startup seq
758 cdr.DriveState = DRIVESTATE_STANDBY;
759 cdr.StatP &= ~STATUS_SEEK;
760 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
764 case DRIVESTATE_LID_OPEN:
765 case DRIVESTATE_RESCAN_CD:
766 // no disk or busy with the initial scan, allowed cmds are limited
767 not_ready = CMD_WHILE_NOT_READY;
771 switch (Cmd | not_ready) {
773 case CdlNop + CMD_WHILE_NOT_READY:
774 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
775 cdr.StatP &= ~STATUS_SHELLOPEN;
779 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
780 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
782 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
783 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))
785 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
786 if (++cdr.errorRetryhack > 100)
788 error = ERROR_BAD_ARGNUM;
793 for (i = 0; i < 3; i++)
794 set_loc[i] = btoi(cdr.Param[i]);
795 memcpy(cdr.SetSector, set_loc, 3);
796 cdr.SetSector[3] = 0;
797 cdr.SetlocPending = 1;
798 cdr.errorRetryhack = 0;
807 cdr.FastBackward = 0;
811 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
813 if (ParamC != 0 && cdr.Param[0] != 0) {
814 int track = btoi( cdr.Param[0] );
816 if (track <= cdr.ResultTN[1])
817 cdr.CurTrack = track;
819 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
821 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
822 for (i = 0; i < 3; i++)
823 set_loc[i] = cdr.ResultTD[2 - i];
824 seekTime = cdrSeekTime(set_loc);
825 memcpy(cdr.SetSectorPlay, set_loc, 3);
828 else if (cdr.SetlocPending) {
829 seekTime = cdrSeekTime(cdr.SetSector);
830 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
833 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
834 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
836 cdr.SetlocPending = 0;
839 Rayman: detect track changes
842 Twisted Metal 2: skip PREGAP + starting accurate SubQ
843 - plays tracks without retry play
845 Wild 9: skip PREGAP + starting accurate SubQ
846 - plays tracks without retry play
848 Find_CurTrack(cdr.SetSectorPlay);
849 generate_subq(cdr.SetSectorPlay);
850 cdr.LocL[0] = LOCL_INVALID;
851 cdr.SubqForwardSectors = 1;
852 cdr.TrackChanged = FALSE;
853 cdr.FileChannelSelected = 0;
855 cdr.ReportDelay = 60;
859 CDR_play(cdr.SetSectorPlay);
861 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
863 // BIOS player - set flag again
865 cdr.DriveState = DRIVESTATE_PLAY_READ;
867 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
872 // TODO: error 80 if stopped
875 // GameShark CD Player: Calls 2x + Play 2x
877 cdr.FastBackward = 0;
883 // GameShark CD Player: Calls 2x + Play 2x
884 cdr.FastBackward = 1;
889 if (cdr.DriveState != DRIVESTATE_STOPPED) {
890 error = ERROR_BAD_ARGNUM;
893 cdr.DriveState = DRIVESTATE_STANDBY;
894 second_resp_time = cdReadTime * 125 / 2;
898 case CdlStandby + CMD_PART2:
904 // grab time for current track
905 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
907 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
908 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
909 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
914 SetPlaySeekRead(cdr.StatP, 0);
915 cdr.StatP &= ~STATUS_ROTATING;
916 cdr.LocL[0] = LOCL_INVALID;
918 second_resp_time = 0x800;
919 if (cdr.DriveState != DRIVESTATE_STOPPED)
920 second_resp_time = cdReadTime * 30 / 2;
922 cdr.DriveState = DRIVESTATE_STOPPED;
925 case CdlStop + CMD_PART2:
930 if (cdr.AdpcmActive) {
933 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 1); // flush adpcm
938 // how the drive maintains the position while paused is quite
939 // complicated, this is the minimum to make "Bedlam" happy
940 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
944 Gundam Battle Assault 2: much slower (*)
945 - Fixes boot, gameplay
947 Hokuto no Ken 2: slower
948 - Fixes intro + subtitles
950 InuYasha - Feudal Fairy Tale: slower
953 /* Gameblabla - Tightening the timings (as taken from Duckstation).
954 * The timings from Duckstation are based upon hardware tests.
955 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
956 * seems to be timing sensitive as it can depend on the CPU's clock speed.
958 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
960 second_resp_time = 7000;
964 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
966 SetPlaySeekRead(cdr.StatP, 0);
967 cdr.DriveState = DRIVESTATE_PAUSED;
970 case CdlPause + CMD_PART2:
975 case CdlReset + CMD_WHILE_NOT_READY:
978 SetPlaySeekRead(cdr.StatP, 0);
979 cdr.LocL[0] = LOCL_INVALID;
980 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
981 cdr.DriveState = DRIVESTATE_PAUSED;
983 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
984 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
985 second_resp_time = not_ready ? 70000 : 4100000;
989 case CdlReset + CMD_PART2:
990 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
996 SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
1001 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1002 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1006 cdr.FilterFile = cdr.Param[0];
1007 cdr.FilterChannel = cdr.Param[1];
1008 cdr.FileChannelSelected = 0;
1012 case CdlSetmode + CMD_WHILE_NOT_READY:
1013 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1014 cdr.Mode = cdr.Param[0];
1018 case CdlGetparam + CMD_WHILE_NOT_READY:
1019 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1021 cdr.Result[1] = cdr.Mode;
1023 cdr.Result[3] = cdr.FilterFile;
1024 cdr.Result[4] = cdr.FilterChannel;
1028 if (cdr.LocL[0] == LOCL_INVALID) {
1033 memcpy(cdr.Result, cdr.LocL, 8);
1038 memcpy(&cdr.Result, &cdr.subq, 8);
1041 case CdlReadT: // SetSession?
1043 second_resp_time = cdReadTime * 290 / 4;
1047 case CdlReadT + CMD_PART2:
1052 if (CDR_getTN(cdr.ResultTN) == -1) {
1056 cdr.Result[1] = itob(cdr.ResultTN[0]);
1057 cdr.Result[2] = itob(cdr.ResultTN[1]);
1061 cdr.Track = btoi(cdr.Param[0]);
1062 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1063 error = ERROR_BAD_ARGVAL;
1067 cdr.Result[1] = itob(cdr.ResultTD[2]);
1068 cdr.Result[2] = itob(cdr.ResultTD[1]);
1070 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1077 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1079 seekTime = cdrSeekTime(cdr.SetSector);
1080 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1081 cdr.DriveState = DRIVESTATE_SEEK;
1083 Crusaders of Might and Magic = 0.5x-4x
1084 - fix cutscene speech start
1086 Eggs of Steel = 2x-?
1090 - fix cutscene speech
1095 second_resp_time = cdReadTime + seekTime;
1099 case CdlSeekL + CMD_PART2:
1100 case CdlSeekP + CMD_PART2:
1101 SetPlaySeekRead(cdr.StatP, 0);
1102 cdr.Result[0] = cdr.StatP;
1105 Find_CurTrack(cdr.SetSectorPlay);
1106 read_ok = ReadTrack(cdr.SetSectorPlay);
1107 if (read_ok && (buf = CDR_getBuffer()))
1108 memcpy(cdr.LocL, buf, 8);
1109 UpdateSubq(cdr.SetSectorPlay);
1110 cdr.DriveState = DRIVESTATE_STANDBY;
1111 cdr.TrackChanged = FALSE;
1112 cdr.LastReadSeekCycles = psxRegs.cycle;
1116 case CdlTest + CMD_WHILE_NOT_READY:
1117 switch (cdr.Param[0]) {
1118 case 0x20: // System Controller ROM Version
1120 memcpy(cdr.Result, Test20, 4);
1124 memcpy(cdr.Result, Test22, 4);
1126 case 0x23: case 0x24:
1128 memcpy(cdr.Result, Test23, 4);
1134 second_resp_time = 20480;
1137 case CdlID + CMD_PART2:
1139 cdr.Result[0] = cdr.StatP;
1144 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1145 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1146 cdr.Result[1] = 0xc0;
1150 cdr.Result[1] |= 0x10;
1151 if (CdromId[0] == '\0')
1152 cdr.Result[1] |= 0x80;
1154 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1155 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1156 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1158 /* This adds the string "PCSX" in Playstation bios boot screen */
1159 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1164 case CdlInit + CMD_WHILE_NOT_READY:
1167 SetPlaySeekRead(cdr.StatP, 0);
1168 // yes, it really sets STATUS_SHELLOPEN
1169 cdr.StatP |= STATUS_SHELLOPEN;
1170 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1171 set_event(PSXINT_CDRLID, 20480);
1176 case CdlGetQ + CMD_WHILE_NOT_READY:
1180 case CdlReadToc + CMD_WHILE_NOT_READY:
1181 cdr.LocL[0] = LOCL_INVALID;
1182 second_resp_time = cdReadTime * 180 / 4;
1186 case CdlReadToc + CMD_PART2:
1187 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1193 if (cdr.Reading && !cdr.SetlocPending)
1196 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1198 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1199 // Read* acts as play for cdda tracks in cdda mode
1203 if (cdr.SetlocPending) {
1204 seekTime = cdrSeekTime(cdr.SetSector);
1205 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1206 cdr.SetlocPending = 0;
1209 cdr.FileChannelSelected = 0;
1210 cdr.AdpcmActive = 0;
1212 // Fighting Force 2 - update subq time immediately
1214 UpdateSubq(cdr.SetSectorPlay);
1215 cdr.LocL[0] = LOCL_INVALID;
1216 cdr.SubqForwardSectors = 1;
1217 cdr.sectorsRead = 0;
1218 cdr.DriveState = DRIVESTATE_SEEK;
1220 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1222 if (Config.hacks.cdr_read_timing)
1223 cycles = cdrAlignTimingHack(cycles);
1224 CDRPLAYREAD_INT(cycles, 1);
1226 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1232 error = ERROR_INVALIDCMD;
1237 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1238 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1239 IrqStat = DiskError;
1240 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1244 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1245 cdr.DriveState = DRIVESTATE_STANDBY;
1246 cdr.StatP |= STATUS_ROTATING;
1249 if (second_resp_time) {
1250 cdr.CmdInProgress = Cmd | 0x100;
1251 set_event(PSXINT_CDR, second_resp_time);
1253 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1254 cdr.CmdInProgress = cdr.Cmd;
1255 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1258 setIrq(IrqStat, Cmd);
1262 #define ssat32_to_16(v) \
1263 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1265 #define ssat32_to_16(v) do { \
1266 if (v < -32768) v = -32768; \
1267 else if (v > 32767) v = 32767; \
1271 static void cdrPrepCdda(s16 *buf, int samples)
1273 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1275 for (i = 0; i < samples; i++) {
1276 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1277 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1282 static void cdrReadInterruptSetResult(unsigned char result)
1285 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1286 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1287 cdr.CmdInProgress, cdr.IrqStat);
1288 cdr.Irq1Pending = result;
1292 cdr.Result[0] = result;
1293 setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1296 static void cdrUpdateTransferBuf(const u8 *buf)
1300 memcpy(cdr.Transfer, buf, DATA_SIZE);
1301 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1302 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1303 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1304 if (cdr.FifoOffset < 2048 + 12)
1305 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1308 static void cdrReadInterrupt(void)
1310 const struct { u8 file, chan, mode, coding; } *subhdr;
1311 const u8 *buf = NULL;
1312 int deliver_data = 1;
1317 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1318 msfiAdd(subqPos, cdr.SubqForwardSectors);
1319 UpdateSubq(subqPos);
1320 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1321 cdr.SubqForwardSectors++;
1322 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1326 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1327 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1328 cdr.DriveState = DRIVESTATE_PLAY_READ;
1331 read_ok = ReadTrack(cdr.SetSectorPlay);
1333 buf = CDR_getBuffer();
1338 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1339 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1340 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1343 memcpy(cdr.LocL, buf, 8);
1345 if (!cdr.IrqStat && !cdr.Irq1Pending)
1346 cdrUpdateTransferBuf(buf);
1348 subhdr = (void *)(buf + 4);
1350 // try to process as adpcm
1351 if (!(cdr.Mode & MODE_STRSND))
1353 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1355 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1356 subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1357 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1359 if (subhdr->chan & 0xe0) { // ?
1360 if (subhdr->chan != 0xff)
1361 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1364 if (!cdr.FileChannelSelected) {
1365 cdr.CurFile = subhdr->file;
1366 cdr.CurChannel = subhdr->chan;
1367 cdr.FileChannelSelected = 1;
1369 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1372 // accepted as adpcm
1377 is_start = !cdr.AdpcmActive;
1378 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, is_start);
1379 if (cdr.AdpcmActive)
1380 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, is_start);
1383 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1387 Croc 2: $40 - only FORM1 (*)
1388 Judge Dredd: $C8 - only FORM1 (*)
1389 Sim Theme Park - no adpcm at all (zero)
1393 cdrReadInterruptSetResult(cdr.StatP);
1395 msfiAdd(cdr.SetSectorPlay, 1);
1397 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1403 bit 2 - adpcm active
1404 bit 5 - 1 result ready
1406 bit 7 - 1 command being processed
1409 unsigned char cdrRead0(void) {
1411 cdr.Ctrl |= cdr.AdpcmActive << 2;
1412 cdr.Ctrl |= cdr.ResultReady << 5;
1414 cdr.Ctrl |= 0x40; // data fifo not empty
1416 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1419 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1421 return psxHu8(0x1800) = cdr.Ctrl;
1424 void cdrWrite0(unsigned char rt) {
1425 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1427 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1430 unsigned char cdrRead1(void) {
1431 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1432 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1436 if (cdr.ResultP == cdr.ResultC)
1437 cdr.ResultReady = 0;
1439 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1441 return psxHu8(0x1801);
1444 void cdrWrite1(unsigned char rt) {
1445 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1446 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1448 switch (cdr.Ctrl & 3) {
1452 cdr.AttenuatorRightToRightT = rt;
1458 #ifdef CDR_LOG_CMD_IRQ
1459 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1462 SysPrintf(" Param[%d] = {", cdr.ParamC);
1463 for (i = 0; i < cdr.ParamC; i++)
1464 SysPrintf(" %x,", cdr.Param[i]);
1471 cdr.ResultReady = 0;
1474 if (!cdr.CmdInProgress) {
1475 cdr.CmdInProgress = rt;
1476 // should be something like 12k + controller delays
1477 set_event(PSXINT_CDR, 5000);
1480 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1481 rt, cdr.Cmd, cdr.CmdInProgress);
1482 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1483 cdr.CmdInProgress = rt;
1489 unsigned char cdrRead2(void) {
1490 unsigned char ret = cdr.Transfer[0x920];
1492 if (cdr.FifoOffset < cdr.FifoSize)
1493 ret = cdr.Transfer[cdr.FifoOffset++];
1495 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1497 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1501 void cdrWrite2(unsigned char rt) {
1502 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1503 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1505 switch (cdr.Ctrl & 3) {
1507 if (cdr.ParamC < 8) // FIXME: size and wrapping
1508 cdr.Param[cdr.ParamC++] = rt;
1512 setIrq(cdr.IrqStat, 0x1005);
1515 cdr.AttenuatorLeftToLeftT = rt;
1518 cdr.AttenuatorRightToLeftT = rt;
1523 unsigned char cdrRead3(void) {
1525 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1527 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1529 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1530 return psxHu8(0x1803);
1533 void cdrWrite3(unsigned char rt) {
1534 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1536 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1538 switch (cdr.Ctrl & 3) {
1542 if (cdr.IrqStat & rt) {
1543 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1544 + psxRegs.intCycle[PSXINT_CDR].cycle;
1545 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1546 #ifdef CDR_LOG_CMD_IRQ
1547 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1548 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1549 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1551 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1552 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1555 if (cdr.CmdInProgress) {
1556 c = 2048 - (psxRegs.cycle - nextCycle);
1557 c = MAX_VALUE(c, 512);
1559 set_event(PSXINT_CDR, c);
1568 cdr.AttenuatorLeftToRightT = rt;
1572 log_unhandled("Mute ADPCM?\n");
1574 ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1575 rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1576 if (ll == cdr.AttenuatorLeftToLeft &&
1577 lr == cdr.AttenuatorLeftToRight &&
1578 rl == cdr.AttenuatorRightToLeft &&
1579 rr == cdr.AttenuatorRightToRight)
1581 cdr.AttenuatorLeftToLeftT = ll; cdr.AttenuatorLeftToRightT = lr;
1582 cdr.AttenuatorRightToLeftT = rl; cdr.AttenuatorRightToRightT = rr;
1583 CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1584 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1590 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1591 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1593 else if (rt & 0x80) {
1594 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1595 case MODE_SIZE_2328:
1597 cdr.FifoOffset = 12;
1598 cdr.FifoSize = 2048 + 12;
1601 case MODE_SIZE_2340:
1604 cdr.FifoSize = 2340;
1608 else if (!(rt & 0xc0))
1609 cdr.FifoOffset = DATA_SIZE; // fifo empty
1612 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1613 u32 cdsize, max_words;
1618 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1619 if (cdr.FifoOffset == 0) {
1621 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1626 switch (chcr & 0x71000000) {
1628 ptr = getDmaRam(madr, &max_words);
1629 if (ptr == INVALID_PTR) {
1630 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1634 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1637 GS CDX: Enhancement CD crash
1640 - Spams DMA3 and gets buffer overrun
1642 size = DATA_SIZE - cdr.FifoOffset;
1645 if (size > max_words * 4)
1646 size = max_words * 4;
1649 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1650 cdr.FifoOffset += size;
1652 if (size < cdsize) {
1653 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1654 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1656 psxCpu->Clear(madr, cdsize / 4);
1658 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1660 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1662 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1663 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1667 psxRegs.cycle += (cdsize/4) * 24 - 20;
1672 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1676 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1680 void cdrDmaInterrupt(void)
1682 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1684 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1689 static void getCdInfo(void)
1693 CDR_getTN(cdr.ResultTN);
1694 CDR_getTD(0, cdr.SetSectorEnd);
1695 tmp = cdr.SetSectorEnd[0];
1696 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1697 cdr.SetSectorEnd[2] = tmp;
1701 memset(&cdr, 0, sizeof(cdr));
1704 cdr.FilterChannel = 0;
1706 cdr.IrqStat = NoIntr;
1707 cdr.FifoOffset = DATA_SIZE; // fifo empty
1709 CDR_getStatus(&stat);
1710 if (stat.Status & STATUS_SHELLOPEN) {
1711 cdr.DriveState = DRIVESTATE_LID_OPEN;
1712 cdr.StatP = STATUS_SHELLOPEN;
1714 else if (CdromId[0] == '\0') {
1715 cdr.DriveState = DRIVESTATE_STOPPED;
1719 cdr.DriveState = DRIVESTATE_STANDBY;
1720 cdr.StatP = STATUS_ROTATING;
1723 // BIOS player - default values
1724 cdr.AttenuatorLeftToLeft = 0x80;
1725 cdr.AttenuatorLeftToRight = 0x00;
1726 cdr.AttenuatorRightToLeft = 0x00;
1727 cdr.AttenuatorRightToRight = 0x80;
1728 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1729 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1734 int cdrFreeze(void *f, int Mode) {
1738 if (Mode == 0 && !Config.Cdda)
1741 cdr.freeze_ver = 0x63647202;
1742 gzfreeze(&cdr, sizeof(cdr));
1745 cdr.ParamP = cdr.ParamC;
1746 tmp = cdr.FifoOffset;
1749 gzfreeze(&tmp, sizeof(tmp));
1752 u8 ll = 0, lr = 0, rl = 0, rr = 0;
1755 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1756 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1757 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1758 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1760 // read right sub data
1761 tmpp[0] = btoi(cdr.Prev[0]);
1762 tmpp[1] = btoi(cdr.Prev[1]);
1763 tmpp[2] = btoi(cdr.Prev[2]);
1768 if (cdr.freeze_ver < 0x63647202)
1769 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1771 Find_CurTrack(cdr.SetSectorPlay);
1773 CDR_play(cdr.SetSectorPlay);
1776 ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToLeft,
1777 rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1778 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1784 void LidInterrupt(void) {
1786 cdrLidSeekInterrupt();