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;
723 CDR_LOG_I("cmd %02x with irqstat %x\n",
724 cdr.CmdInProgress, cdr.IrqStat);
727 if (cdr.Irq1Pending) {
728 // hand out the "newest" sector, according to nocash
729 cdrUpdateTransferBuf(CDR_getBuffer());
730 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
731 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
732 cdr.CmdInProgress, cdr.Irq1Pending);
734 cdr.Result[0] = cdr.Irq1Pending;
736 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
742 cdr.Result[0] = cdr.StatP;
744 Cmd = cdr.CmdInProgress;
745 cdr.CmdInProgress = 0;
754 switch (cdr.DriveState) {
755 case DRIVESTATE_PREPARE_CD:
757 // Syphon filter 2 expects commands to work shortly after it sees
758 // STATUS_ROTATING, so give up trying to emulate the startup seq
759 cdr.DriveState = DRIVESTATE_STANDBY;
760 cdr.StatP &= ~STATUS_SEEK;
761 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
765 case DRIVESTATE_LID_OPEN:
766 case DRIVESTATE_RESCAN_CD:
767 // no disk or busy with the initial scan, allowed cmds are limited
768 not_ready = CMD_WHILE_NOT_READY;
772 switch (Cmd | not_ready) {
774 case CdlNop + CMD_WHILE_NOT_READY:
775 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
776 cdr.StatP &= ~STATUS_SHELLOPEN;
780 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
781 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
783 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
784 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))
786 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
787 if (++cdr.errorRetryhack > 100)
789 error = ERROR_BAD_ARGNUM;
794 for (i = 0; i < 3; i++)
795 set_loc[i] = btoi(cdr.Param[i]);
796 memcpy(cdr.SetSector, set_loc, 3);
797 cdr.SetSector[3] = 0;
798 cdr.SetlocPending = 1;
799 cdr.errorRetryhack = 0;
808 cdr.FastBackward = 0;
812 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
814 if (ParamC != 0 && cdr.Param[0] != 0) {
815 int track = btoi( cdr.Param[0] );
817 if (track <= cdr.ResultTN[1])
818 cdr.CurTrack = track;
820 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
822 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
823 for (i = 0; i < 3; i++)
824 set_loc[i] = cdr.ResultTD[2 - i];
825 seekTime = cdrSeekTime(set_loc);
826 memcpy(cdr.SetSectorPlay, set_loc, 3);
829 else if (cdr.SetlocPending) {
830 seekTime = cdrSeekTime(cdr.SetSector);
831 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
834 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
835 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
837 cdr.SetlocPending = 0;
840 Rayman: detect track changes
843 Twisted Metal 2: skip PREGAP + starting accurate SubQ
844 - plays tracks without retry play
846 Wild 9: skip PREGAP + starting accurate SubQ
847 - plays tracks without retry play
849 Find_CurTrack(cdr.SetSectorPlay);
850 generate_subq(cdr.SetSectorPlay);
851 cdr.LocL[0] = LOCL_INVALID;
852 cdr.SubqForwardSectors = 1;
853 cdr.TrackChanged = FALSE;
854 cdr.FileChannelSelected = 0;
856 cdr.ReportDelay = 60;
860 CDR_play(cdr.SetSectorPlay);
862 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
864 // BIOS player - set flag again
866 cdr.DriveState = DRIVESTATE_PLAY_READ;
868 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
873 // TODO: error 80 if stopped
876 // GameShark CD Player: Calls 2x + Play 2x
878 cdr.FastBackward = 0;
884 // GameShark CD Player: Calls 2x + Play 2x
885 cdr.FastBackward = 1;
890 if (cdr.DriveState != DRIVESTATE_STOPPED) {
891 error = ERROR_BAD_ARGNUM;
894 cdr.DriveState = DRIVESTATE_STANDBY;
895 second_resp_time = cdReadTime * 125 / 2;
899 case CdlStandby + CMD_PART2:
905 // grab time for current track
906 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
908 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
909 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
910 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
915 SetPlaySeekRead(cdr.StatP, 0);
916 cdr.StatP &= ~STATUS_ROTATING;
917 cdr.LocL[0] = LOCL_INVALID;
919 second_resp_time = 0x800;
920 if (cdr.DriveState != DRIVESTATE_STOPPED)
921 second_resp_time = cdReadTime * 30 / 2;
923 cdr.DriveState = DRIVESTATE_STOPPED;
926 case CdlStop + CMD_PART2:
931 if (cdr.AdpcmActive) {
934 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 1); // flush adpcm
939 // how the drive maintains the position while paused is quite
940 // complicated, this is the minimum to make "Bedlam" happy
941 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
945 Gundam Battle Assault 2: much slower (*)
946 - Fixes boot, gameplay
948 Hokuto no Ken 2: slower
949 - Fixes intro + subtitles
951 InuYasha - Feudal Fairy Tale: slower
954 /* Gameblabla - Tightening the timings (as taken from Duckstation).
955 * The timings from Duckstation are based upon hardware tests.
956 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
957 * seems to be timing sensitive as it can depend on the CPU's clock speed.
959 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
961 second_resp_time = 7000;
965 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
967 SetPlaySeekRead(cdr.StatP, 0);
968 DriveStateOld = cdr.DriveState;
969 cdr.DriveState = DRIVESTATE_PAUSED;
970 if (DriveStateOld == DRIVESTATE_SEEK) {
971 // According to Duckstation this fails, but the
972 // exact conditions and effects are not clear.
973 // Moto Racer World Tour seems to rely on this.
974 // For now assume pause works anyway, just errors out.
975 error = ERROR_NOTREADY;
980 case CdlPause + CMD_PART2:
985 case CdlReset + CMD_WHILE_NOT_READY:
988 SetPlaySeekRead(cdr.StatP, 0);
989 cdr.LocL[0] = LOCL_INVALID;
990 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
991 cdr.DriveState = DRIVESTATE_PAUSED;
993 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
994 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
995 second_resp_time = not_ready ? 70000 : 4100000;
999 case CdlReset + CMD_PART2:
1000 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
1006 SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
1011 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1012 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1016 cdr.FilterFile = cdr.Param[0];
1017 cdr.FilterChannel = cdr.Param[1];
1018 cdr.FileChannelSelected = 0;
1022 case CdlSetmode + CMD_WHILE_NOT_READY:
1023 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1024 cdr.Mode = cdr.Param[0];
1028 case CdlGetparam + CMD_WHILE_NOT_READY:
1029 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1031 cdr.Result[1] = cdr.Mode;
1033 cdr.Result[3] = cdr.FilterFile;
1034 cdr.Result[4] = cdr.FilterChannel;
1038 if (cdr.LocL[0] == LOCL_INVALID) {
1043 memcpy(cdr.Result, cdr.LocL, 8);
1048 memcpy(&cdr.Result, &cdr.subq, 8);
1051 case CdlReadT: // SetSession?
1053 second_resp_time = cdReadTime * 290 / 4;
1057 case CdlReadT + CMD_PART2:
1062 if (CDR_getTN(cdr.ResultTN) == -1) {
1066 cdr.Result[1] = itob(cdr.ResultTN[0]);
1067 cdr.Result[2] = itob(cdr.ResultTN[1]);
1071 cdr.Track = btoi(cdr.Param[0]);
1072 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1073 error = ERROR_BAD_ARGVAL;
1077 cdr.Result[1] = itob(cdr.ResultTD[2]);
1078 cdr.Result[2] = itob(cdr.ResultTD[1]);
1080 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1087 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1089 seekTime = cdrSeekTime(cdr.SetSector);
1090 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1091 cdr.DriveState = DRIVESTATE_SEEK;
1093 Crusaders of Might and Magic = 0.5x-4x
1094 - fix cutscene speech start
1096 Eggs of Steel = 2x-?
1100 - fix cutscene speech
1105 second_resp_time = cdReadTime + seekTime;
1109 case CdlSeekL + CMD_PART2:
1110 case CdlSeekP + CMD_PART2:
1111 SetPlaySeekRead(cdr.StatP, 0);
1112 cdr.Result[0] = cdr.StatP;
1115 Find_CurTrack(cdr.SetSectorPlay);
1116 read_ok = ReadTrack(cdr.SetSectorPlay);
1117 if (read_ok && (buf = CDR_getBuffer()))
1118 memcpy(cdr.LocL, buf, 8);
1119 UpdateSubq(cdr.SetSectorPlay);
1120 cdr.DriveState = DRIVESTATE_STANDBY;
1121 cdr.TrackChanged = FALSE;
1122 cdr.LastReadSeekCycles = psxRegs.cycle;
1126 case CdlTest + CMD_WHILE_NOT_READY:
1127 switch (cdr.Param[0]) {
1128 case 0x20: // System Controller ROM Version
1130 memcpy(cdr.Result, Test20, 4);
1134 memcpy(cdr.Result, Test22, 4);
1136 case 0x23: case 0x24:
1138 memcpy(cdr.Result, Test23, 4);
1144 second_resp_time = 20480;
1147 case CdlID + CMD_PART2:
1149 cdr.Result[0] = cdr.StatP;
1154 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1155 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1156 cdr.Result[1] = 0xc0;
1160 cdr.Result[1] |= 0x10;
1161 if (CdromId[0] == '\0')
1162 cdr.Result[1] |= 0x80;
1164 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1165 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1166 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1168 /* This adds the string "PCSX" in Playstation bios boot screen */
1169 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1174 case CdlInit + CMD_WHILE_NOT_READY:
1177 SetPlaySeekRead(cdr.StatP, 0);
1178 // yes, it really sets STATUS_SHELLOPEN
1179 cdr.StatP |= STATUS_SHELLOPEN;
1180 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1181 set_event(PSXINT_CDRLID, 20480);
1186 case CdlGetQ + CMD_WHILE_NOT_READY:
1190 case CdlReadToc + CMD_WHILE_NOT_READY:
1191 cdr.LocL[0] = LOCL_INVALID;
1192 second_resp_time = cdReadTime * 180 / 4;
1196 case CdlReadToc + CMD_PART2:
1197 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1203 if (cdr.Reading && !cdr.SetlocPending)
1206 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1208 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1209 // Read* acts as play for cdda tracks in cdda mode
1213 if (cdr.SetlocPending) {
1214 seekTime = cdrSeekTime(cdr.SetSector);
1215 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1216 cdr.SetlocPending = 0;
1219 cdr.FileChannelSelected = 0;
1220 cdr.AdpcmActive = 0;
1222 // Fighting Force 2 - update subq time immediately
1224 UpdateSubq(cdr.SetSectorPlay);
1225 cdr.LocL[0] = LOCL_INVALID;
1226 cdr.SubqForwardSectors = 1;
1227 cdr.sectorsRead = 0;
1228 cdr.DriveState = DRIVESTATE_SEEK;
1230 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1232 if (Config.hacks.cdr_read_timing)
1233 cycles = cdrAlignTimingHack(cycles);
1234 CDRPLAYREAD_INT(cycles, 1);
1236 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1242 error = ERROR_INVALIDCMD;
1247 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1248 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1249 IrqStat = DiskError;
1250 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1254 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1255 cdr.DriveState = DRIVESTATE_STANDBY;
1256 cdr.StatP |= STATUS_ROTATING;
1259 if (second_resp_time) {
1260 cdr.CmdInProgress = Cmd | 0x100;
1261 set_event(PSXINT_CDR, second_resp_time);
1263 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1264 cdr.CmdInProgress = cdr.Cmd;
1265 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1268 setIrq(IrqStat, Cmd);
1272 #define ssat32_to_16(v) \
1273 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1275 #define ssat32_to_16(v) do { \
1276 if (v < -32768) v = -32768; \
1277 else if (v > 32767) v = 32767; \
1281 static void cdrPrepCdda(s16 *buf, int samples)
1283 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1285 for (i = 0; i < samples; i++) {
1286 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1287 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
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.IrqStat);
1298 cdr.Irq1Pending = result;
1302 cdr.Result[0] = result;
1303 setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1306 static void cdrUpdateTransferBuf(const u8 *buf)
1310 memcpy(cdr.Transfer, buf, DATA_SIZE);
1311 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1312 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1313 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1314 if (cdr.FifoOffset < 2048 + 12)
1315 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1318 static void cdrReadInterrupt(void)
1320 const struct { u8 file, chan, mode, coding; } *subhdr;
1321 const u8 *buf = NULL;
1322 int deliver_data = 1;
1327 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1328 msfiAdd(subqPos, cdr.SubqForwardSectors);
1329 UpdateSubq(subqPos);
1330 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1331 cdr.SubqForwardSectors++;
1332 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1336 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1337 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1338 cdr.DriveState = DRIVESTATE_PLAY_READ;
1341 read_ok = ReadTrack(cdr.SetSectorPlay);
1343 buf = CDR_getBuffer();
1348 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1349 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1350 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1353 memcpy(cdr.LocL, buf, 8);
1355 if (!cdr.IrqStat && !cdr.Irq1Pending)
1356 cdrUpdateTransferBuf(buf);
1358 subhdr = (void *)(buf + 4);
1360 // try to process as adpcm
1361 if (!(cdr.Mode & MODE_STRSND))
1363 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1365 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1366 subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1367 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1369 if (subhdr->chan & 0xe0) { // ?
1370 if (subhdr->chan != 0xff)
1371 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1374 if (!cdr.FileChannelSelected) {
1375 cdr.CurFile = subhdr->file;
1376 cdr.CurChannel = subhdr->chan;
1377 cdr.FileChannelSelected = 1;
1379 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1382 // accepted as adpcm
1387 is_start = !cdr.AdpcmActive;
1388 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, is_start);
1389 if (cdr.AdpcmActive)
1390 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, is_start);
1393 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1397 Croc 2: $40 - only FORM1 (*)
1398 Judge Dredd: $C8 - only FORM1 (*)
1399 Sim Theme Park - no adpcm at all (zero)
1403 cdrReadInterruptSetResult(cdr.StatP);
1405 msfiAdd(cdr.SetSectorPlay, 1);
1407 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1413 bit 2 - adpcm active
1414 bit 5 - 1 result ready
1416 bit 7 - 1 command being processed
1419 unsigned char cdrRead0(void) {
1421 cdr.Ctrl |= cdr.AdpcmActive << 2;
1422 cdr.Ctrl |= cdr.ResultReady << 5;
1424 cdr.Ctrl |= 0x40; // data fifo not empty
1426 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1429 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1431 return psxHu8(0x1800) = cdr.Ctrl;
1434 void cdrWrite0(unsigned char rt) {
1435 CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
1437 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1440 unsigned char cdrRead1(void) {
1441 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1442 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1446 if (cdr.ResultP == cdr.ResultC)
1447 cdr.ResultReady = 0;
1449 CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1451 return psxHu8(0x1801);
1454 void cdrWrite1(unsigned char rt) {
1455 const char *rnames[] = { "0.cmd", "1.smd", "2.smc", "3.arr" }; (void)rnames;
1456 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1458 switch (cdr.Ctrl & 3) {
1462 cdr.AttenuatorRightToRightT = rt;
1468 #ifdef CDR_LOG_CMD_IRQ
1469 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1472 SysPrintf(" Param[%d] = {", cdr.ParamC);
1473 for (i = 0; i < cdr.ParamC; i++)
1474 SysPrintf(" %x,", cdr.Param[i]);
1477 SysPrintf(" @%08x\n", psxRegs.pc);
1480 cdr.ResultReady = 0;
1483 if (!cdr.CmdInProgress) {
1484 cdr.CmdInProgress = rt;
1485 // should be something like 12k + controller delays
1486 set_event(PSXINT_CDR, 5000);
1489 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1490 rt, cdr.Cmd, cdr.CmdInProgress);
1491 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1492 cdr.CmdInProgress = rt;
1498 unsigned char cdrRead2(void) {
1499 unsigned char ret = cdr.Transfer[0x920];
1501 if (cdr.FifoOffset < cdr.FifoSize)
1502 ret = cdr.Transfer[cdr.FifoOffset++];
1504 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1506 CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
1510 void cdrWrite2(unsigned char rt) {
1511 const char *rnames[] = { "0.prm", "1.ien", "2.all", "3.arl" }; (void)rnames;
1512 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1514 switch (cdr.Ctrl & 3) {
1516 if (cdr.ParamC < 8) // FIXME: size and wrapping
1517 cdr.Param[cdr.ParamC++] = rt;
1521 setIrq(cdr.IrqStat, 0x1005);
1524 cdr.AttenuatorLeftToLeftT = rt;
1527 cdr.AttenuatorRightToLeftT = rt;
1532 unsigned char cdrRead3(void) {
1534 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1536 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1538 CDR_LOG_IO("cdr r3.%d.%s: %02x\n", cdr.Ctrl & 3,
1539 (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1540 return psxHu8(0x1803);
1543 void cdrWrite3(unsigned char rt) {
1544 const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
1546 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1548 switch (cdr.Ctrl & 3) {
1552 if (cdr.IrqStat & rt) {
1553 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1554 + psxRegs.intCycle[PSXINT_CDR].cycle;
1555 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1556 #ifdef CDR_LOG_CMD_IRQ
1557 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1558 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1559 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1561 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1562 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1565 if (cdr.CmdInProgress) {
1566 c = 2048 - (psxRegs.cycle - nextCycle);
1567 c = MAX_VALUE(c, 512);
1569 set_event(PSXINT_CDR, c);
1578 cdr.AttenuatorLeftToRightT = rt;
1582 log_unhandled("Mute ADPCM?\n");
1584 ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1585 rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1586 if (ll == cdr.AttenuatorLeftToLeft &&
1587 lr == cdr.AttenuatorLeftToRight &&
1588 rl == cdr.AttenuatorRightToLeft &&
1589 rr == cdr.AttenuatorRightToRight)
1591 cdr.AttenuatorLeftToLeft = ll; cdr.AttenuatorLeftToRight = lr;
1592 cdr.AttenuatorRightToLeft = rl; cdr.AttenuatorRightToRight = rr;
1593 CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1594 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1600 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1601 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1603 else if (rt & 0x80) {
1604 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1605 case MODE_SIZE_2328:
1607 cdr.FifoOffset = 12;
1608 cdr.FifoSize = 2048 + 12;
1611 case MODE_SIZE_2340:
1614 cdr.FifoSize = 2340;
1618 else if (!(rt & 0xc0))
1619 cdr.FifoOffset = DATA_SIZE; // fifo empty
1622 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1623 u32 cdsize, max_words;
1628 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1629 if (cdr.FifoOffset == 0) {
1631 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1636 switch (chcr & 0x71000000) {
1639 ptr = getDmaRam(madr, &max_words);
1640 if (ptr == INVALID_PTR) {
1641 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1645 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1648 GS CDX: Enhancement CD crash
1651 - Spams DMA3 and gets buffer overrun
1653 size = DATA_SIZE - cdr.FifoOffset;
1656 if (size > max_words * 4)
1657 size = max_words * 4;
1660 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1661 cdr.FifoOffset += size;
1663 if (size < cdsize) {
1664 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1665 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1667 psxCpu->Clear(madr, cdsize / 4);
1669 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1671 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1673 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1674 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1678 psxRegs.cycle += (cdsize/4) * 24 - 20;
1683 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1687 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1691 void cdrDmaInterrupt(void)
1693 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1695 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1700 static void getCdInfo(void)
1704 CDR_getTN(cdr.ResultTN);
1705 CDR_getTD(0, cdr.SetSectorEnd);
1706 tmp = cdr.SetSectorEnd[0];
1707 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1708 cdr.SetSectorEnd[2] = tmp;
1712 memset(&cdr, 0, sizeof(cdr));
1715 cdr.FilterChannel = 0;
1717 cdr.IrqStat = NoIntr;
1718 cdr.FifoOffset = DATA_SIZE; // fifo empty
1720 CDR_getStatus(&stat);
1721 if (stat.Status & STATUS_SHELLOPEN) {
1722 cdr.DriveState = DRIVESTATE_LID_OPEN;
1723 cdr.StatP = STATUS_SHELLOPEN;
1725 else if (CdromId[0] == '\0') {
1726 cdr.DriveState = DRIVESTATE_STOPPED;
1730 cdr.DriveState = DRIVESTATE_STANDBY;
1731 cdr.StatP = STATUS_ROTATING;
1734 // BIOS player - default values
1735 cdr.AttenuatorLeftToLeft = 0x80;
1736 cdr.AttenuatorLeftToRight = 0x00;
1737 cdr.AttenuatorRightToLeft = 0x00;
1738 cdr.AttenuatorRightToRight = 0x80;
1739 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1740 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1745 int cdrFreeze(void *f, int Mode) {
1749 if (Mode == 0 && !Config.Cdda)
1752 cdr.freeze_ver = 0x63647202;
1753 gzfreeze(&cdr, sizeof(cdr));
1756 cdr.ParamP = cdr.ParamC;
1757 tmp = cdr.FifoOffset;
1760 gzfreeze(&tmp, sizeof(tmp));
1763 u8 ll = 0, lr = 0, rl = 0, rr = 0;
1766 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1767 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1768 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1769 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1771 // read right sub data
1772 tmpp[0] = btoi(cdr.Prev[0]);
1773 tmpp[1] = btoi(cdr.Prev[1]);
1774 tmpp[2] = btoi(cdr.Prev[2]);
1779 if (cdr.freeze_ver < 0x63647202)
1780 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1782 Find_CurTrack(cdr.SetSectorPlay);
1784 CDR_play(cdr.SetSectorPlay);
1787 ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToLeft,
1788 rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1789 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1795 void LidInterrupt(void) {
1797 cdrLidSeekInterrupt();