1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
18 ***************************************************************************/
21 * Handles all CD-ROM registers and functions.
29 #include "psxevents.h"
30 #include "arm_features.h"
34 #define CDR_LOG SysPrintf
39 #define CDR_LOG_I SysPrintf
41 #define CDR_LOG_I(fmt, ...) \
42 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
45 #define CDR_LOG_IO SysPrintf
47 #define CDR_LOG_IO(...)
49 //#define CDR_LOG_CMD_IRQ
52 // unused members maintain savesate compatibility
53 unsigned char unused0;
54 unsigned char unused1;
56 unsigned char unused2;
62 unsigned char Transfer[DATA_SIZE];
66 unsigned char Relative[3];
67 unsigned char Absolute[3];
69 unsigned char TrackChanged;
70 unsigned char ReportDelay;
71 unsigned char unused3;
72 unsigned short sectorsRead;
73 unsigned int freeze_ver;
75 unsigned char Prev[4];
76 unsigned char Param[8];
77 unsigned char Result[16];
81 unsigned char ResultC;
82 unsigned char ResultP;
83 unsigned char ResultReady;
85 unsigned char SubqForwardSectors;
86 unsigned char SetlocPending;
89 unsigned char ResultTN[6];
90 unsigned char ResultTD[4];
91 unsigned char SetSectorPlay[4];
92 unsigned char SetSectorEnd[4];
93 unsigned char SetSector[4];
98 unsigned char FileChannelSelected;
99 unsigned char CurFile, CurChannel;
100 int FilterFile, FilterChannel;
101 unsigned char LocL[8];
112 u32 LastReadSeekCycles;
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_INVALIDARG (1<<5) // 0x20
213 #define ERROR_SHELLOPEN (1<<3) // 0x08
215 // 1x = 75 sectors per second
216 // PSXCLK = 1 sec in the ps
217 // so (PSXCLK / 75) = cdr read time (linuzappz)
218 #define cdReadTime (PSXCLK / 75)
220 #define LOCL_INVALID 0xff
221 #define SUBQ_FORWARD_SECTORS 2u
224 DRIVESTATE_STANDBY = 0, // pause, play, read
226 DRIVESTATE_RESCAN_CD,
227 DRIVESTATE_PREPARE_CD,
231 static struct CdrStat stat;
233 static unsigned int msf2sec(const u8 *msf) {
234 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
237 // for that weird psemu API..
238 static unsigned int fsm2sec(const u8 *msf) {
239 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
242 static void sec2msf(unsigned int s, u8 *msf) {
243 msf[0] = s / 75 / 60;
244 s = s - msf[0] * 75 * 60;
250 // cdrPlayReadInterrupt
251 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
253 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
255 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
257 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
258 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
259 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
262 #define StopReading() { \
264 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
267 #define StopCdda() { \
268 if (cdr.Play && !Config.Cdda) CDR_stop(); \
270 cdr.FastForward = 0; \
271 cdr.FastBackward = 0; \
274 #define SetPlaySeekRead(x, f) { \
275 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
279 #define SetResultSize(size) { \
281 cdr.ResultC = size; \
282 cdr.ResultReady = 1; \
285 static void setIrq(int log_cmd)
287 if (cdr.Stat & cdr.Reg2)
288 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
290 #ifdef CDR_LOG_CMD_IRQ
294 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
295 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
296 for (i = 0; i < cdr.ResultC; i++)
297 SysPrintf("%02x ", cdr.Result[i]);
303 // timing used in this function was taken from tests on real hardware
304 // (yes it's slow, but you probably don't want to modify it)
305 void cdrLidSeekInterrupt(void)
307 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
309 switch (cdr.DriveState) {
311 case DRIVESTATE_STANDBY:
314 SetPlaySeekRead(cdr.StatP, 0);
316 if (CDR_getStatus(&stat) == -1)
319 if (stat.Status & STATUS_SHELLOPEN)
321 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
322 cdr.DriveState = DRIVESTATE_LID_OPEN;
323 set_event(PSXINT_CDRLID, 0x800);
327 case DRIVESTATE_LID_OPEN:
328 if (CDR_getStatus(&stat) == -1)
329 stat.Status &= ~STATUS_SHELLOPEN;
332 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
333 SetPlaySeekRead(cdr.StatP, 0);
334 cdr.StatP |= STATUS_SHELLOPEN;
336 // IIRC this sometimes doesn't happen on real hw
337 // (when lots of commands are sent?)
341 cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
342 cdr.Result[1] = ERROR_SHELLOPEN;
343 cdr.Stat = DiskError;
346 if (cdr.CmdInProgress) {
347 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
348 cdr.CmdInProgress = 0;
350 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
351 cdr.Result[1] = ERROR_NOTREADY;
352 cdr.Stat = DiskError;
356 set_event(PSXINT_CDRLID, cdReadTime * 30);
359 else if (cdr.StatP & STATUS_ROTATING) {
360 cdr.StatP &= ~STATUS_ROTATING;
362 else if (!(stat.Status & STATUS_SHELLOPEN)) {
366 // cdr.StatP STATUS_SHELLOPEN is "sticky"
367 // and is only cleared by CdlNop
369 cdr.DriveState = DRIVESTATE_RESCAN_CD;
370 set_event(PSXINT_CDRLID, cdReadTime * 105);
375 set_event(PSXINT_CDRLID, cdReadTime * 3);
378 case DRIVESTATE_RESCAN_CD:
379 cdr.StatP |= STATUS_ROTATING;
380 cdr.DriveState = DRIVESTATE_PREPARE_CD;
382 // this is very long on real hardware, over 6 seconds
383 // make it a bit faster here...
384 set_event(PSXINT_CDRLID, cdReadTime * 150);
387 case DRIVESTATE_PREPARE_CD:
388 if (cdr.StatP & STATUS_SEEK) {
389 SetPlaySeekRead(cdr.StatP, 0);
390 cdr.DriveState = DRIVESTATE_STANDBY;
393 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
394 set_event(PSXINT_CDRLID, cdReadTime * 26);
400 static void Find_CurTrack(const u8 *time)
404 current = msf2sec(time);
406 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
407 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
408 sect = fsm2sec(cdr.ResultTD);
409 if (sect - current >= 150)
414 static void generate_subq(const u8 *time)
416 unsigned char start[3], next[3];
417 unsigned int this_s, start_s, next_s, pregap;
420 CDR_getTD(cdr.CurTrack, start);
421 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
423 CDR_getTD(cdr.CurTrack + 1, next);
426 // last track - cd size
428 next[0] = cdr.SetSectorEnd[2];
429 next[1] = cdr.SetSectorEnd[1];
430 next[2] = cdr.SetSectorEnd[0];
433 this_s = msf2sec(time);
434 start_s = fsm2sec(start);
435 next_s = fsm2sec(next);
437 cdr.TrackChanged = FALSE;
439 if (next_s - this_s < pregap) {
440 cdr.TrackChanged = TRUE;
447 relative_s = this_s - start_s;
448 if (relative_s < 0) {
450 relative_s = -relative_s;
452 sec2msf(relative_s, cdr.subq.Relative);
454 cdr.subq.Track = itob(cdr.CurTrack);
455 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
456 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
457 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
458 cdr.subq.Absolute[0] = itob(time[0]);
459 cdr.subq.Absolute[1] = itob(time[1]);
460 cdr.subq.Absolute[2] = itob(time[2]);
463 static int ReadTrack(const u8 *time)
465 unsigned char tmp[3];
468 tmp[0] = itob(time[0]);
469 tmp[1] = itob(time[1]);
470 tmp[2] = itob(time[2]);
472 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
474 if (memcmp(cdr.Prev, tmp, 3) == 0)
477 read_ok = CDR_readTrack(tmp);
479 memcpy(cdr.Prev, tmp, 3);
483 static void UpdateSubq(const u8 *time)
485 const struct SubQ *subq;
486 int s = MSF2SECT(time[0], time[1], time[2]);
492 subq = (struct SubQ *)CDR_getBufferSub(s);
493 if (subq != NULL && cdr.CurTrack == 1) {
494 crc = calcCrc((u8 *)subq + 12, 10);
495 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
496 cdr.subq.Track = subq->TrackNumber;
497 cdr.subq.Index = subq->IndexNumber;
498 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
499 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
502 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
503 time[0], time[1], time[2]);
510 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
511 cdr.subq.Track, cdr.subq.Index,
512 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
513 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
516 static void cdrPlayInterrupt_Autopause()
519 boolean abs_lev_chselect;
522 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
523 CDR_LOG_I("autopause\n");
526 cdr.Result[0] = cdr.StatP;
528 setIrq(0x1000); // 0x1000 just for logging purposes
531 SetPlaySeekRead(cdr.StatP, 0);
533 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
534 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
537 cdr.Result[0] = cdr.StatP;
538 cdr.Result[1] = cdr.subq.Track;
539 cdr.Result[2] = cdr.subq.Index;
541 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
543 /* 8 is a hack. For accuracy, it should be 588. */
544 for (i = 0; i < 8; i++)
546 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
548 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
549 abs_lev_max |= abs_lev_chselect << 15;
551 if (cdr.subq.Absolute[2] & 0x10) {
552 cdr.Result[3] = cdr.subq.Relative[0];
553 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
554 cdr.Result[5] = cdr.subq.Relative[2];
557 cdr.Result[3] = cdr.subq.Absolute[0];
558 cdr.Result[4] = cdr.subq.Absolute[1];
559 cdr.Result[5] = cdr.subq.Absolute[2];
561 cdr.Result[6] = abs_lev_max >> 0;
562 cdr.Result[7] = abs_lev_max >> 8;
564 cdr.Stat = DataReady;
572 static int cdrSeekTime(unsigned char *target)
574 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
575 int seekTime = abs(diff) * (cdReadTime / 2000);
576 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
577 seekTime = MAX_VALUE(seekTime, 20000);
579 // need this stupidly long penalty or else Spyro2 intro desyncs
580 // note: if misapplied this breaks MGS cutscenes among other things
581 if (cyclesSinceRS > cdReadTime * 50)
582 seekTime += cdReadTime * 25;
583 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
584 // and then wants some slack time
585 else if (cyclesSinceRS < cdReadTime *3/2)
586 seekTime += cdReadTime;
588 seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
589 CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
590 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime);
594 static u32 cdrAlignTimingHack(u32 cycles)
597 * timing hack for T'ai Fu - Wrath of the Tiger:
598 * The game has a bug where it issues some cdc commands from a low priority
599 * vint handler, however there is a higher priority default bios handler
600 * that acks the vint irq and returns, so game's handler is not reached
601 * (see bios irq handler chains at e004 and the game's irq handling func
602 * at 80036810). For the game to work, vint has to arrive after the bios
603 * vint handler rejects some other irq (of which only cd and rcnt2 are
604 * active), but before the game's handler loop reads I_STAT. The time
605 * window for this is quite small (~1k cycles of so). Apparently this
606 * somehow happens naturally on the real hardware.
608 * Note: always enforcing this breaks other games like Crash PAL version
609 * (inputs get dropped because bios handler doesn't see interrupts).
612 if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
614 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
615 vint_rel += PSXCLK / 60;
616 while ((s32)(vint_rel - cycles) < 0)
617 vint_rel += PSXCLK / 60;
621 static void cdrUpdateTransferBuf(const u8 *buf);
622 static void cdrReadInterrupt(void);
623 static void cdrPrepCdda(s16 *buf, int samples);
624 static void cdrAttenuate(s16 *buf, int samples, int stereo);
626 static void msfiAdd(u8 *msfi, u32 count)
640 static void msfiSub(u8 *msfi, u32 count)
644 if ((s8)msfi[2] < 0) {
647 if ((s8)msfi[1] < 0) {
654 void cdrPlayReadInterrupt(void)
656 cdr.LastReadSeekCycles = psxRegs.cycle;
663 if (!cdr.Play) return;
665 CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
666 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
668 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
669 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
670 CDR_LOG_I("end stop\n");
672 SetPlaySeekRead(cdr.StatP, 0);
673 cdr.TrackChanged = TRUE;
676 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
679 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
680 cdrPlayInterrupt_Autopause();
682 if (!cdr.Muted && cdr.Play && !Config.Cdda) {
683 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
684 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
685 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
688 msfiAdd(cdr.SetSectorPlay, 1);
690 // update for CdlGetlocP/autopause
691 generate_subq(cdr.SetSectorPlay);
693 CDRPLAYREAD_INT(cdReadTime, 0);
696 #define CMD_PART2 0x100
697 #define CMD_WHILE_NOT_READY 0x200
699 void cdrInterrupt(void) {
700 int start_rotating = 0;
702 u32 cycles, seekTime = 0;
703 u32 second_resp_time = 0;
713 CDR_LOG_I("cmd %02x with irqstat %x\n",
714 cdr.CmdInProgress, cdr.Stat);
717 if (cdr.Irq1Pending) {
718 // hand out the "newest" sector, according to nocash
719 cdrUpdateTransferBuf(CDR_getBuffer());
720 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
721 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
722 cdr.CmdInProgress, cdr.Irq1Pending);
724 cdr.Result[0] = cdr.Irq1Pending;
725 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
733 cdr.Result[0] = cdr.StatP;
734 cdr.Stat = Acknowledge;
736 Cmd = cdr.CmdInProgress;
737 cdr.CmdInProgress = 0;
746 switch (cdr.DriveState) {
747 case DRIVESTATE_PREPARE_CD:
749 // Syphon filter 2 expects commands to work shortly after it sees
750 // STATUS_ROTATING, so give up trying to emulate the startup seq
751 cdr.DriveState = DRIVESTATE_STANDBY;
752 cdr.StatP &= ~STATUS_SEEK;
753 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
757 case DRIVESTATE_LID_OPEN:
758 case DRIVESTATE_RESCAN_CD:
759 // no disk or busy with the initial scan, allowed cmds are limited
760 not_ready = CMD_WHILE_NOT_READY;
764 switch (Cmd | not_ready) {
766 case CdlNop + CMD_WHILE_NOT_READY:
767 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
768 cdr.StatP &= ~STATUS_SHELLOPEN;
772 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
773 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
775 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
776 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))
778 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
779 if (++cdr.errorRetryhack > 100)
781 error = ERROR_INVALIDARG;
786 for (i = 0; i < 3; i++)
787 set_loc[i] = btoi(cdr.Param[i]);
788 memcpy(cdr.SetSector, set_loc, 3);
789 cdr.SetSector[3] = 0;
790 cdr.SetlocPending = 1;
791 cdr.errorRetryhack = 0;
800 cdr.FastBackward = 0;
804 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
806 if (ParamC != 0 && cdr.Param[0] != 0) {
807 int track = btoi( cdr.Param[0] );
809 if (track <= cdr.ResultTN[1])
810 cdr.CurTrack = track;
812 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
814 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
815 for (i = 0; i < 3; i++)
816 set_loc[i] = cdr.ResultTD[2 - i];
817 seekTime = cdrSeekTime(set_loc);
818 memcpy(cdr.SetSectorPlay, set_loc, 3);
821 else if (cdr.SetlocPending) {
822 seekTime = cdrSeekTime(cdr.SetSector);
823 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
826 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
827 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
829 cdr.SetlocPending = 0;
832 Rayman: detect track changes
835 Twisted Metal 2: skip PREGAP + starting accurate SubQ
836 - plays tracks without retry play
838 Wild 9: skip PREGAP + starting accurate SubQ
839 - plays tracks without retry play
841 Find_CurTrack(cdr.SetSectorPlay);
842 generate_subq(cdr.SetSectorPlay);
843 cdr.LocL[0] = LOCL_INVALID;
844 cdr.SubqForwardSectors = 1;
845 cdr.TrackChanged = FALSE;
846 cdr.FileChannelSelected = 0;
848 cdr.ReportDelay = 60;
852 CDR_play(cdr.SetSectorPlay);
854 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
856 // BIOS player - set flag again
859 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
864 // TODO: error 80 if stopped
867 // GameShark CD Player: Calls 2x + Play 2x
869 cdr.FastBackward = 0;
875 // GameShark CD Player: Calls 2x + Play 2x
876 cdr.FastBackward = 1;
881 if (cdr.DriveState != DRIVESTATE_STOPPED) {
882 error = ERROR_INVALIDARG;
885 second_resp_time = cdReadTime * 125 / 2;
889 case CdlStandby + CMD_PART2:
895 // grab time for current track
896 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
898 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
899 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
900 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
905 SetPlaySeekRead(cdr.StatP, 0);
906 cdr.StatP &= ~STATUS_ROTATING;
907 cdr.LocL[0] = LOCL_INVALID;
909 second_resp_time = 0x800;
910 if (cdr.DriveState == DRIVESTATE_STANDBY)
911 second_resp_time = cdReadTime * 30 / 2;
913 cdr.DriveState = DRIVESTATE_STOPPED;
916 case CdlStop + CMD_PART2:
924 // how the drive maintains the position while paused is quite
925 // complicated, this is the minimum to make "Bedlam" happy
926 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
930 Gundam Battle Assault 2: much slower (*)
931 - Fixes boot, gameplay
933 Hokuto no Ken 2: slower
934 - Fixes intro + subtitles
936 InuYasha - Feudal Fairy Tale: slower
939 /* Gameblabla - Tightening the timings (as taken from Duckstation).
940 * The timings from Duckstation are based upon hardware tests.
941 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
942 * seems to be timing sensitive as it can depend on the CPU's clock speed.
944 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
946 second_resp_time = 7000;
950 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
952 SetPlaySeekRead(cdr.StatP, 0);
955 case CdlPause + CMD_PART2:
960 case CdlReset + CMD_WHILE_NOT_READY:
963 SetPlaySeekRead(cdr.StatP, 0);
964 cdr.LocL[0] = LOCL_INVALID;
966 cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
967 second_resp_time = not_ready ? 70000 : 4100000;
971 case CdlReset + CMD_PART2:
972 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
985 cdr.FilterFile = cdr.Param[0];
986 cdr.FilterChannel = cdr.Param[1];
990 case CdlSetmode + CMD_WHILE_NOT_READY:
991 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
992 cdr.Mode = cdr.Param[0];
996 case CdlGetparam + CMD_WHILE_NOT_READY:
997 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
999 cdr.Result[1] = cdr.Mode;
1001 cdr.Result[3] = cdr.FilterFile;
1002 cdr.Result[4] = cdr.FilterChannel;
1006 if (cdr.LocL[0] == LOCL_INVALID) {
1011 memcpy(cdr.Result, cdr.LocL, 8);
1016 memcpy(&cdr.Result, &cdr.subq, 8);
1019 case CdlReadT: // SetSession?
1021 second_resp_time = cdReadTime * 290 / 4;
1025 case CdlReadT + CMD_PART2:
1026 cdr.Stat = Complete;
1031 if (CDR_getTN(cdr.ResultTN) == -1) {
1032 cdr.Stat = DiskError;
1033 cdr.Result[0] |= STATUS_ERROR;
1035 cdr.Stat = Acknowledge;
1036 cdr.Result[1] = itob(cdr.ResultTN[0]);
1037 cdr.Result[2] = itob(cdr.ResultTN[1]);
1042 cdr.Track = btoi(cdr.Param[0]);
1044 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1045 cdr.Stat = DiskError;
1046 cdr.Result[0] |= STATUS_ERROR;
1048 cdr.Stat = Acknowledge;
1049 cdr.Result[0] = cdr.StatP;
1050 cdr.Result[1] = itob(cdr.ResultTD[2]);
1051 cdr.Result[2] = itob(cdr.ResultTD[1]);
1052 /* According to Nocash's documentation, the function doesn't care about ff.
1053 * This can be seen also in Mednafen's implementation. */
1054 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1062 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1064 seekTime = cdrSeekTime(cdr.SetSector);
1065 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1067 Crusaders of Might and Magic = 0.5x-4x
1068 - fix cutscene speech start
1070 Eggs of Steel = 2x-?
1074 - fix cutscene speech
1079 second_resp_time = cdReadTime + seekTime;
1083 case CdlSeekL + CMD_PART2:
1084 case CdlSeekP + CMD_PART2:
1085 SetPlaySeekRead(cdr.StatP, 0);
1086 cdr.Result[0] = cdr.StatP;
1087 cdr.Stat = Complete;
1089 Find_CurTrack(cdr.SetSectorPlay);
1090 read_ok = ReadTrack(cdr.SetSectorPlay);
1091 if (read_ok && (buf = CDR_getBuffer()))
1092 memcpy(cdr.LocL, buf, 8);
1093 UpdateSubq(cdr.SetSectorPlay);
1094 cdr.TrackChanged = FALSE;
1095 cdr.LastReadSeekCycles = psxRegs.cycle;
1099 case CdlTest + CMD_WHILE_NOT_READY:
1100 switch (cdr.Param[0]) {
1101 case 0x20: // System Controller ROM Version
1103 memcpy(cdr.Result, Test20, 4);
1107 memcpy(cdr.Result, Test22, 4);
1109 case 0x23: case 0x24:
1111 memcpy(cdr.Result, Test23, 4);
1117 second_resp_time = 20480;
1120 case CdlID + CMD_PART2:
1122 cdr.Result[0] = cdr.StatP;
1127 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1128 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1129 cdr.Result[1] = 0xc0;
1133 cdr.Result[1] |= 0x10;
1134 if (CdromId[0] == '\0')
1135 cdr.Result[1] |= 0x80;
1137 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1138 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1139 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1141 /* This adds the string "PCSX" in Playstation bios boot screen */
1142 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1143 cdr.Stat = Complete;
1147 case CdlInit + CMD_WHILE_NOT_READY:
1150 SetPlaySeekRead(cdr.StatP, 0);
1151 // yes, it really sets STATUS_SHELLOPEN
1152 cdr.StatP |= STATUS_SHELLOPEN;
1153 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1154 set_event(PSXINT_CDRLID, 20480);
1159 case CdlGetQ + CMD_WHILE_NOT_READY:
1163 case CdlReadToc + CMD_WHILE_NOT_READY:
1164 cdr.LocL[0] = LOCL_INVALID;
1165 second_resp_time = cdReadTime * 180 / 4;
1169 case CdlReadToc + CMD_PART2:
1170 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1171 cdr.Stat = Complete;
1176 if (cdr.Reading && !cdr.SetlocPending)
1179 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1181 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1182 // Read* acts as play for cdda tracks in cdda mode
1186 if (cdr.SetlocPending) {
1187 seekTime = cdrSeekTime(cdr.SetSector);
1188 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1189 cdr.SetlocPending = 0;
1192 cdr.FileChannelSelected = 0;
1193 cdr.AdpcmActive = 0;
1195 // Fighting Force 2 - update subq time immediately
1197 UpdateSubq(cdr.SetSectorPlay);
1198 cdr.LocL[0] = LOCL_INVALID;
1199 cdr.SubqForwardSectors = 1;
1200 cdr.sectorsRead = 0;
1202 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1204 if (Config.hacks.cdr_read_timing)
1205 cycles = cdrAlignTimingHack(cycles);
1206 CDRPLAYREAD_INT(cycles, 1);
1208 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1214 error = ERROR_INVALIDCMD;
1219 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1220 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1221 cdr.Stat = DiskError;
1222 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1226 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1227 cdr.DriveState = DRIVESTATE_STANDBY;
1228 cdr.StatP |= STATUS_ROTATING;
1231 if (second_resp_time) {
1232 cdr.CmdInProgress = Cmd | 0x100;
1233 set_event(PSXINT_CDR, second_resp_time);
1235 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1236 cdr.CmdInProgress = cdr.Cmd;
1237 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1244 #define ssat32_to_16(v) \
1245 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1247 #define ssat32_to_16(v) do { \
1248 if (v < -32768) v = -32768; \
1249 else if (v > 32767) v = 32767; \
1253 static void cdrPrepCdda(s16 *buf, int samples)
1255 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1257 for (i = 0; i < samples; i++) {
1258 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1259 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1264 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1267 int ll = cdr.AttenuatorLeftToLeft;
1268 int lr = cdr.AttenuatorLeftToRight;
1269 int rl = cdr.AttenuatorRightToLeft;
1270 int rr = cdr.AttenuatorRightToRight;
1272 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1275 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1279 for (i = 0; i < samples; i++) {
1282 l = (l * ll + r * rl) >> 7;
1283 r = (r * rr + l * lr) >> 7;
1291 for (i = 0; i < samples; i++) {
1293 l = l * (ll + rl) >> 7;
1294 //r = r * (rr + lr) >> 7;
1302 static void cdrReadInterruptSetResult(unsigned char result)
1305 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1306 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1307 cdr.CmdInProgress, cdr.Stat);
1308 cdr.Irq1Pending = result;
1312 cdr.Result[0] = result;
1313 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1317 static void cdrUpdateTransferBuf(const u8 *buf)
1321 memcpy(cdr.Transfer, buf, DATA_SIZE);
1322 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1323 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1324 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1325 if (cdr.FifoOffset < 2048 + 12)
1326 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1329 static void cdrReadInterrupt(void)
1331 const struct { u8 file, chan, mode, coding; } *subhdr;
1332 const u8 *buf = NULL;
1333 int deliver_data = 1;
1337 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1338 msfiAdd(subqPos, cdr.SubqForwardSectors);
1339 UpdateSubq(subqPos);
1340 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1341 cdr.SubqForwardSectors++;
1342 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1346 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1347 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1350 read_ok = ReadTrack(cdr.SetSectorPlay);
1352 buf = CDR_getBuffer();
1357 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1358 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1361 memcpy(cdr.LocL, buf, 8);
1363 if (!cdr.Stat && !cdr.Irq1Pending)
1364 cdrUpdateTransferBuf(buf);
1366 subhdr = (void *)(buf + 4);
1368 // try to process as adpcm
1369 if (!(cdr.Mode & MODE_STRSND))
1371 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1373 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1374 subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1375 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1377 if (subhdr->chan & 0xe0) { // ?
1378 if (subhdr->chan != 0xff)
1379 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1382 if (!cdr.FileChannelSelected) {
1383 cdr.CurFile = subhdr->file;
1384 cdr.CurChannel = subhdr->chan;
1385 cdr.FileChannelSelected = 1;
1387 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1390 // accepted as adpcm
1395 if (!cdr.Muted && cdr.AdpcmActive) {
1396 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1397 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 0);
1400 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, !cdr.AdpcmActive);
1403 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1407 Croc 2: $40 - only FORM1 (*)
1408 Judge Dredd: $C8 - only FORM1 (*)
1409 Sim Theme Park - no adpcm at all (zero)
1413 cdrReadInterruptSetResult(cdr.StatP);
1415 msfiAdd(cdr.SetSectorPlay, 1);
1417 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1423 bit 2 - adpcm active
1424 bit 5 - 1 result ready
1426 bit 7 - 1 command being processed
1429 unsigned char cdrRead0(void) {
1431 cdr.Ctrl |= cdr.AdpcmActive << 2;
1432 cdr.Ctrl |= cdr.ResultReady << 5;
1434 cdr.Ctrl |= 0x40; // data fifo not empty
1436 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1439 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1441 return psxHu8(0x1800) = cdr.Ctrl;
1444 void cdrWrite0(unsigned char rt) {
1445 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1447 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1450 unsigned char cdrRead1(void) {
1451 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1452 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1456 if (cdr.ResultP == cdr.ResultC)
1457 cdr.ResultReady = 0;
1459 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1461 return psxHu8(0x1801);
1464 void cdrWrite1(unsigned char rt) {
1465 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1466 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1468 switch (cdr.Ctrl & 3) {
1472 cdr.AttenuatorRightToRightT = rt;
1478 #ifdef CDR_LOG_CMD_IRQ
1479 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1482 SysPrintf(" Param[%d] = {", cdr.ParamC);
1483 for (i = 0; i < cdr.ParamC; i++)
1484 SysPrintf(" %x,", cdr.Param[i]);
1491 cdr.ResultReady = 0;
1494 if (!cdr.CmdInProgress) {
1495 cdr.CmdInProgress = rt;
1496 // should be something like 12k + controller delays
1497 set_event(PSXINT_CDR, 5000);
1500 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1501 rt, cdr.Cmd, cdr.CmdInProgress);
1502 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1503 cdr.CmdInProgress = rt;
1509 unsigned char cdrRead2(void) {
1510 unsigned char ret = cdr.Transfer[0x920];
1512 if (cdr.FifoOffset < cdr.FifoSize)
1513 ret = cdr.Transfer[cdr.FifoOffset++];
1515 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1517 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1521 void cdrWrite2(unsigned char rt) {
1522 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1523 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1525 switch (cdr.Ctrl & 3) {
1527 if (cdr.ParamC < 8) // FIXME: size and wrapping
1528 cdr.Param[cdr.ParamC++] = rt;
1535 cdr.AttenuatorLeftToLeftT = rt;
1538 cdr.AttenuatorRightToLeftT = rt;
1543 unsigned char cdrRead3(void) {
1545 psxHu8(0x1803) = cdr.Stat | 0xE0;
1547 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1549 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1550 return psxHu8(0x1803);
1553 void cdrWrite3(unsigned char rt) {
1554 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1555 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1557 switch (cdr.Ctrl & 3) {
1561 if (cdr.Stat & rt) {
1562 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1563 + psxRegs.intCycle[PSXINT_CDR].cycle;
1564 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1565 #ifdef CDR_LOG_CMD_IRQ
1566 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1567 !!pending, cdr.CmdInProgress,
1568 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1570 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1571 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1574 if (cdr.CmdInProgress) {
1575 c = 2048 - (psxRegs.cycle - nextCycle);
1576 c = MAX_VALUE(c, 512);
1578 set_event(PSXINT_CDR, c);
1587 cdr.AttenuatorLeftToRightT = rt;
1591 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1592 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1593 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1594 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
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) {
1638 ptr = getDmaRam(madr, &max_words);
1639 if (ptr == INVALID_PTR) {
1640 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1644 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1647 GS CDX: Enhancement CD crash
1650 - Spams DMA3 and gets buffer overrun
1652 size = DATA_SIZE - cdr.FifoOffset;
1655 if (size > max_words * 4)
1656 size = max_words * 4;
1659 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1660 cdr.FifoOffset += size;
1662 if (size < cdsize) {
1663 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1664 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1666 psxCpu->Clear(madr, cdsize / 4);
1668 set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1670 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1672 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1673 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1677 psxRegs.cycle += (cdsize/4) * 24 - 20;
1682 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1686 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1690 void cdrDmaInterrupt(void)
1692 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1694 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1699 static void getCdInfo(void)
1703 CDR_getTN(cdr.ResultTN);
1704 CDR_getTD(0, cdr.SetSectorEnd);
1705 tmp = cdr.SetSectorEnd[0];
1706 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1707 cdr.SetSectorEnd[2] = tmp;
1711 memset(&cdr, 0, sizeof(cdr));
1714 cdr.FilterChannel = 0;
1717 cdr.FifoOffset = DATA_SIZE; // fifo empty
1719 CDR_getStatus(&stat);
1720 if (stat.Status & STATUS_SHELLOPEN) {
1721 cdr.DriveState = DRIVESTATE_LID_OPEN;
1722 cdr.StatP = STATUS_SHELLOPEN;
1724 else if (CdromId[0] == '\0') {
1725 cdr.DriveState = DRIVESTATE_STOPPED;
1729 cdr.DriveState = DRIVESTATE_STANDBY;
1730 cdr.StatP = STATUS_ROTATING;
1733 // BIOS player - default values
1734 cdr.AttenuatorLeftToLeft = 0x80;
1735 cdr.AttenuatorLeftToRight = 0x00;
1736 cdr.AttenuatorRightToLeft = 0x00;
1737 cdr.AttenuatorRightToRight = 0x80;
1742 int cdrFreeze(void *f, int Mode) {
1746 if (Mode == 0 && !Config.Cdda)
1749 cdr.freeze_ver = 0x63647202;
1750 gzfreeze(&cdr, sizeof(cdr));
1753 cdr.ParamP = cdr.ParamC;
1754 tmp = cdr.FifoOffset;
1757 gzfreeze(&tmp, sizeof(tmp));
1762 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1763 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1764 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1765 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1767 // read right sub data
1768 tmpp[0] = btoi(cdr.Prev[0]);
1769 tmpp[1] = btoi(cdr.Prev[1]);
1770 tmpp[2] = btoi(cdr.Prev[2]);
1775 if (cdr.freeze_ver < 0x63647202)
1776 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1778 Find_CurTrack(cdr.SetSectorPlay);
1780 CDR_play(cdr.SetSectorPlay);
1783 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1784 // old versions did not latch Reg2, have to fixup..
1785 if (cdr.Reg2 == 0) {
1786 SysPrintf("cdrom: fixing up old savestate\n");
1789 // also did not save Attenuator..
1790 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1791 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1793 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1801 void LidInterrupt(void) {
1803 cdrLidSeekInterrupt();