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.
27 #include "arm_features.h"
31 #define CDR_LOG SysPrintf
36 #define CDR_LOG_I SysPrintf
38 #define CDR_LOG_I log_unhandled
41 #define CDR_LOG_IO SysPrintf
43 #define CDR_LOG_IO(...)
45 //#define CDR_LOG_CMD_IRQ
48 // unused members maintain savesate compatibility
49 unsigned char unused0;
50 unsigned char unused1;
52 unsigned char unused2;
58 unsigned char Transfer[DATA_SIZE];
62 unsigned char Relative[3];
63 unsigned char Absolute[3];
65 unsigned char TrackChanged;
66 unsigned char unused3[3];
67 unsigned int freeze_ver;
69 unsigned char Prev[4];
70 unsigned char Param[8];
71 unsigned char Result[16];
75 unsigned char ResultC;
76 unsigned char ResultP;
77 unsigned char ResultReady;
79 unsigned char unused4;
80 unsigned char SetlocPending;
83 unsigned char ResultTN[6];
84 unsigned char ResultTD[4];
85 unsigned char SetSectorPlay[4];
86 unsigned char SetSectorEnd[4];
87 unsigned char SetSector[4];
91 int Mode, File, Channel;
113 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
114 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
115 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
116 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
118 static s16 read_buf[CD_FRAMESIZE_RAW/2];
120 /* CD-ROM magic numbers */
121 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
126 #define CdlBackward 5
134 #define CdlSetfilter 13
135 #define CdlSetmode 14
136 #define CdlGetparam 15
137 #define CdlGetlocL 16
138 #define CdlGetlocP 17
144 #define CdlSetclock 23
145 #define CdlGetclock 24
151 #define CdlReadToc 30
153 #ifdef CDR_LOG_CMD_IRQ
154 static const char * const CmdName[0x100] = {
155 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
156 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
157 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
158 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
159 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
160 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
161 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
162 "CdlInit", NULL, "CDlReadToc", NULL
166 unsigned char Test04[] = { 0 };
167 unsigned char Test05[] = { 0 };
168 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
169 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
170 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
176 #define Acknowledge 3
181 #define MODE_SPEED (1<<7) // 0x80
182 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
183 #define MODE_SIZE_2340 (1<<5) // 0x20
184 #define MODE_SIZE_2328 (1<<4) // 0x10
185 #define MODE_SIZE_2048 (0<<4) // 0x00
186 #define MODE_SF (1<<3) // 0x08 channel on/off
187 #define MODE_REPORT (1<<2) // 0x04
188 #define MODE_AUTOPAUSE (1<<1) // 0x02
189 #define MODE_CDDA (1<<0) // 0x01
192 #define STATUS_PLAY (1<<7) // 0x80
193 #define STATUS_SEEK (1<<6) // 0x40
194 #define STATUS_READ (1<<5) // 0x20
195 #define STATUS_SHELLOPEN (1<<4) // 0x10
196 #define STATUS_UNKNOWN3 (1<<3) // 0x08
197 #define STATUS_UNKNOWN2 (1<<2) // 0x04
198 #define STATUS_ROTATING (1<<1) // 0x02
199 #define STATUS_ERROR (1<<0) // 0x01
202 #define ERROR_NOTREADY (1<<7) // 0x80
203 #define ERROR_INVALIDCMD (1<<6) // 0x40
204 #define ERROR_INVALIDARG (1<<5) // 0x20
206 // 1x = 75 sectors per second
207 // PSXCLK = 1 sec in the ps
208 // so (PSXCLK / 75) = cdr read time (linuzappz)
209 #define cdReadTime (PSXCLK / 75)
212 DRIVESTATE_STANDBY = 0, // pause, play, read
214 DRIVESTATE_RESCAN_CD,
215 DRIVESTATE_PREPARE_CD,
219 static struct CdrStat stat;
221 static unsigned int msf2sec(const u8 *msf) {
222 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
225 // for that weird psemu API..
226 static unsigned int fsm2sec(const u8 *msf) {
227 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
230 static void sec2msf(unsigned int s, u8 *msf) {
231 msf[0] = s / 75 / 60;
232 s = s - msf[0] * 75 * 60;
239 #define CDR_INT(eCycle) { \
240 psxRegs.interrupt |= (1 << PSXINT_CDR); \
241 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
242 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
243 new_dyna_set_event(PSXINT_CDR, eCycle); \
246 // cdrPlaySeekReadInterrupt
247 #define CDRPLAYSEEKREAD_INT(eCycle, isFirst) { \
249 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
251 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
253 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
254 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
255 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
258 // cdrLidSeekInterrupt
259 #define CDRLID_INT(eCycle) { \
260 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
261 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
262 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
263 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
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 static void setIrq(int log_cmd)
291 if (cdr.Stat & cdr.Reg2)
292 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
294 #ifdef CDR_LOG_CMD_IRQ
298 SysPrintf("%u cdrom: CDR IRQ=%d cmd %02x stat %02x: ",
299 psxRegs.cycle, !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
300 for (i = 0; i < cdr.ResultC; i++)
301 SysPrintf("%02x ", cdr.Result[i]);
307 // timing used in this function was taken from tests on real hardware
308 // (yes it's slow, but you probably don't want to modify it)
309 void cdrLidSeekInterrupt(void)
311 switch (cdr.DriveState) {
313 case DRIVESTATE_STANDBY:
316 SetPlaySeekRead(cdr.StatP, 0);
318 if (CDR_getStatus(&stat) == -1)
321 if (stat.Status & STATUS_SHELLOPEN)
323 cdr.DriveState = DRIVESTATE_LID_OPEN;
328 case DRIVESTATE_LID_OPEN:
329 if (CDR_getStatus(&stat) == -1)
330 stat.Status &= ~STATUS_SHELLOPEN;
333 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
334 cdr.StatP |= STATUS_SHELLOPEN;
336 // could generate error irq here, but real hardware
337 // only sometimes does that
338 // (not done when lots of commands are sent?)
340 CDRLID_INT(cdReadTime * 30);
343 else if (cdr.StatP & STATUS_ROTATING) {
344 cdr.StatP &= ~STATUS_ROTATING;
346 else if (!(stat.Status & STATUS_SHELLOPEN)) {
350 // cdr.StatP STATUS_SHELLOPEN is "sticky"
351 // and is only cleared by CdlNop
353 cdr.DriveState = DRIVESTATE_RESCAN_CD;
354 CDRLID_INT(cdReadTime * 105);
359 CDRLID_INT(cdReadTime * 3);
362 case DRIVESTATE_RESCAN_CD:
363 cdr.StatP |= STATUS_ROTATING;
364 cdr.DriveState = DRIVESTATE_PREPARE_CD;
366 // this is very long on real hardware, over 6 seconds
367 // make it a bit faster here...
368 CDRLID_INT(cdReadTime * 150);
371 case DRIVESTATE_PREPARE_CD:
372 cdr.StatP |= STATUS_SEEK;
374 cdr.DriveState = DRIVESTATE_STANDBY;
375 CDRLID_INT(cdReadTime * 26);
380 static void Find_CurTrack(const u8 *time)
384 current = msf2sec(time);
386 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
387 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
388 sect = fsm2sec(cdr.ResultTD);
389 if (sect - current >= 150)
394 static void generate_subq(const u8 *time)
396 unsigned char start[3], next[3];
397 unsigned int this_s, start_s, next_s, pregap;
400 CDR_getTD(cdr.CurTrack, start);
401 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
403 CDR_getTD(cdr.CurTrack + 1, next);
406 // last track - cd size
408 next[0] = cdr.SetSectorEnd[2];
409 next[1] = cdr.SetSectorEnd[1];
410 next[2] = cdr.SetSectorEnd[0];
413 this_s = msf2sec(time);
414 start_s = fsm2sec(start);
415 next_s = fsm2sec(next);
417 cdr.TrackChanged = FALSE;
419 if (next_s - this_s < pregap) {
420 cdr.TrackChanged = TRUE;
427 relative_s = this_s - start_s;
428 if (relative_s < 0) {
430 relative_s = -relative_s;
432 sec2msf(relative_s, cdr.subq.Relative);
434 cdr.subq.Track = itob(cdr.CurTrack);
435 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
436 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
437 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
438 cdr.subq.Absolute[0] = itob(time[0]);
439 cdr.subq.Absolute[1] = itob(time[1]);
440 cdr.subq.Absolute[2] = itob(time[2]);
443 static void ReadTrack(const u8 *time) {
444 unsigned char tmp[3];
448 tmp[0] = itob(time[0]);
449 tmp[1] = itob(time[1]);
450 tmp[2] = itob(time[2]);
452 if (memcmp(cdr.Prev, tmp, 3) == 0)
455 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
457 cdr.NoErr = CDR_readTrack(tmp);
458 memcpy(cdr.Prev, tmp, 3);
463 subq = (struct SubQ *)CDR_getBufferSub();
464 if (subq != NULL && cdr.CurTrack == 1) {
465 crc = calcCrc((u8 *)subq + 12, 10);
466 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
467 cdr.subq.Track = subq->TrackNumber;
468 cdr.subq.Index = subq->IndexNumber;
469 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
470 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
473 CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
474 tmp[0], tmp[1], tmp[2]);
481 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
482 cdr.subq.Track, cdr.subq.Index,
483 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
484 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
487 static void cdrPlayInterrupt_Autopause()
490 boolean abs_lev_chselect;
493 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
494 CDR_LOG( "CDDA STOP\n" );
496 // Magic the Gathering
497 // - looping territory cdda
500 //cdr.ResultReady = 1;
501 //cdr.Stat = DataReady;
503 setIrq(0x1000); // 0x1000 just for logging purposes
506 SetPlaySeekRead(cdr.StatP, 0);
508 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
509 cdr.Result[0] = cdr.StatP;
510 cdr.Result[1] = cdr.subq.Track;
511 cdr.Result[2] = cdr.subq.Index;
513 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
515 /* 8 is a hack. For accuracy, it should be 588. */
516 for (i = 0; i < 8; i++)
518 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
520 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
521 abs_lev_max |= abs_lev_chselect << 15;
523 if (cdr.subq.Absolute[2] & 0x10) {
524 cdr.Result[3] = cdr.subq.Relative[0];
525 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
526 cdr.Result[5] = cdr.subq.Relative[2];
529 cdr.Result[3] = cdr.subq.Absolute[0];
530 cdr.Result[4] = cdr.subq.Absolute[1];
531 cdr.Result[5] = cdr.subq.Absolute[2];
534 cdr.Result[6] = abs_lev_max >> 0;
535 cdr.Result[7] = abs_lev_max >> 8;
537 // Rayman: Logo freeze (resultready + dataready)
539 cdr.Stat = DataReady;
546 static int cdrSeekTime(unsigned char *target)
548 int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
549 int seekTime = abs(diff) * (cdReadTime / 200);
552 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
553 * and was unreliable for that game.
554 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
556 * Obviously, this isn't perfect but right now, it should be a bit better.
557 * Games to test this against if you change that setting :
558 * - Driver (titlescreen music delay and retry mission)
559 * - Worms Pinball (Will either not boot or crash in the memory card screen)
560 * - Viewpoint (short pauses if the delay in the ingame music is too long)
562 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
563 * However, 1000000 is not enough for Worms Pinball to reliably boot.
565 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
566 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
570 static void cdrUpdateTransferBuf(const u8 *buf);
571 static void cdrReadInterrupt(void);
572 static void cdrPrepCdda(s16 *buf, int samples);
573 static void cdrAttenuate(s16 *buf, int samples, int stereo);
575 void cdrPlaySeekReadInterrupt(void)
582 if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
584 CDR_LOG_I("cdrom: seek stat hack\n");
585 CDRPLAYSEEKREAD_INT(0x1000, 1);
589 cdr.StatP |= STATUS_ROTATING;
590 SetPlaySeekRead(cdr.StatP, 0);
591 cdr.Result[0] = cdr.StatP;
595 Find_CurTrack(cdr.SetSectorPlay);
596 ReadTrack(cdr.SetSectorPlay);
597 cdr.TrackChanged = FALSE;
601 if (!cdr.Play) return;
603 CDR_LOG( "CDDA - %d:%d:%d\n",
604 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
606 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
607 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
609 SetPlaySeekRead(cdr.StatP, 0);
610 cdr.TrackChanged = TRUE;
613 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
616 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
617 cdrPlayInterrupt_Autopause();
619 if (!cdr.Muted && !Config.Cdda) {
620 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
621 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
622 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
626 cdr.SetSectorPlay[2]++;
627 if (cdr.SetSectorPlay[2] == 75) {
628 cdr.SetSectorPlay[2] = 0;
629 cdr.SetSectorPlay[1]++;
630 if (cdr.SetSectorPlay[1] == 60) {
631 cdr.SetSectorPlay[1] = 0;
632 cdr.SetSectorPlay[0]++;
636 // update for CdlGetlocP/autopause
637 generate_subq(cdr.SetSectorPlay);
639 CDRPLAYSEEKREAD_INT(cdReadTime, 0);
642 #define CMD_PART2 0x100
643 #define CMD_WHILE_NOT_READY 0x200
645 void cdrInterrupt(void) {
646 int start_rotating = 0;
648 unsigned int seekTime = 0;
649 u32 second_resp_time = 0;
657 CDR_LOG_I("%u cdrom: cmd %02x with irqstat %x\n",
658 psxRegs.cycle, cdr.CmdInProgress, cdr.Stat);
661 if (cdr.Irq1Pending) {
662 // hand out the "newest" sector, according to nocash
663 cdrUpdateTransferBuf(CDR_getBuffer());
664 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
665 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
667 cdr.Result[0] = cdr.Irq1Pending;
668 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
676 cdr.Result[0] = cdr.StatP;
677 cdr.Stat = Acknowledge;
679 Cmd = cdr.CmdInProgress;
680 cdr.CmdInProgress = 0;
689 switch (cdr.DriveState) {
690 case DRIVESTATE_LID_OPEN:
691 case DRIVESTATE_RESCAN_CD:
692 case DRIVESTATE_PREPARE_CD:
693 // no disk or busy with the initial scan, allowed cmds are limited
694 not_ready = CMD_WHILE_NOT_READY;
698 switch (Cmd | not_ready) {
700 case CdlNop + CMD_WHILE_NOT_READY:
701 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
702 cdr.StatP &= ~STATUS_SHELLOPEN;
706 case CdlSetloc + CMD_WHILE_NOT_READY:
707 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
709 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
710 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))
712 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
713 error = ERROR_INVALIDARG;
718 for (i = 0; i < 3; i++)
719 set_loc[i] = btoi(cdr.Param[i]);
720 memcpy(cdr.SetSector, set_loc, 3);
721 cdr.SetSector[3] = 0;
722 cdr.SetlocPending = 1;
731 cdr.FastBackward = 0;
735 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
737 if (ParamC != 0 && cdr.Param[0] != 0) {
738 int track = btoi( cdr.Param[0] );
740 if (track <= cdr.ResultTN[1])
741 cdr.CurTrack = track;
743 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
745 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
746 for (i = 0; i < 3; i++)
747 set_loc[i] = cdr.ResultTD[2 - i];
748 seekTime = cdrSeekTime(set_loc);
749 memcpy(cdr.SetSectorPlay, set_loc, 3);
752 else if (cdr.SetlocPending) {
753 seekTime = cdrSeekTime(cdr.SetSector);
754 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
757 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
758 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
760 cdr.SetlocPending = 0;
763 Rayman: detect track changes
766 Twisted Metal 2: skip PREGAP + starting accurate SubQ
767 - plays tracks without retry play
769 Wild 9: skip PREGAP + starting accurate SubQ
770 - plays tracks without retry play
772 Find_CurTrack(cdr.SetSectorPlay);
773 ReadTrack(cdr.SetSectorPlay);
774 cdr.TrackChanged = FALSE;
778 CDR_play(cdr.SetSectorPlay);
780 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
782 // BIOS player - set flag again
785 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
790 // TODO: error 80 if stopped
793 // GameShark CD Player: Calls 2x + Play 2x
795 cdr.FastBackward = 0;
801 // GameShark CD Player: Calls 2x + Play 2x
802 cdr.FastBackward = 1;
807 if (cdr.DriveState != DRIVESTATE_STOPPED) {
808 error = ERROR_INVALIDARG;
811 second_resp_time = cdReadTime * 125 / 2;
815 case CdlStandby + CMD_PART2:
821 // grab time for current track
822 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
824 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
825 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
826 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
831 SetPlaySeekRead(cdr.StatP, 0);
832 cdr.StatP &= ~STATUS_ROTATING;
834 second_resp_time = 0x800;
835 if (cdr.DriveState == DRIVESTATE_STANDBY)
836 second_resp_time = cdReadTime * 30 / 2;
838 cdr.DriveState = DRIVESTATE_STOPPED;
841 case CdlStop + CMD_PART2:
849 Gundam Battle Assault 2: much slower (*)
850 - Fixes boot, gameplay
852 Hokuto no Ken 2: slower
853 - Fixes intro + subtitles
855 InuYasha - Feudal Fairy Tale: slower
858 /* Gameblabla - Tightening the timings (as taken from Duckstation).
859 * The timings from Duckstation are based upon hardware tests.
860 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
861 * seems to be timing sensitive as it can depend on the CPU's clock speed.
863 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
865 second_resp_time = 7000;
869 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
871 SetPlaySeekRead(cdr.StatP, 0);
874 case CdlPause + CMD_PART2:
879 case CdlReset + CMD_WHILE_NOT_READY:
882 SetPlaySeekRead(cdr.StatP, 0);
884 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
885 second_resp_time = not_ready ? 70000 : 4100000;
889 case CdlReset + CMD_PART2:
890 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
903 cdr.File = cdr.Param[0];
904 cdr.Channel = cdr.Param[1];
908 case CdlSetmode + CMD_WHILE_NOT_READY:
909 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
910 cdr.Mode = cdr.Param[0];
914 case CdlGetparam + CMD_WHILE_NOT_READY:
915 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
917 cdr.Result[1] = cdr.Mode;
919 cdr.Result[3] = cdr.File;
920 cdr.Result[4] = cdr.Channel;
925 memcpy(cdr.Result, cdr.Transfer, 8);
930 memcpy(&cdr.Result, &cdr.subq, 8);
933 case CdlReadT: // SetSession?
935 second_resp_time = cdReadTime * 290 / 4;
939 case CdlReadT + CMD_PART2:
945 if (CDR_getTN(cdr.ResultTN) == -1) {
946 cdr.Stat = DiskError;
947 cdr.Result[0] |= STATUS_ERROR;
949 cdr.Stat = Acknowledge;
950 cdr.Result[1] = itob(cdr.ResultTN[0]);
951 cdr.Result[2] = itob(cdr.ResultTN[1]);
956 cdr.Track = btoi(cdr.Param[0]);
958 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
959 cdr.Stat = DiskError;
960 cdr.Result[0] |= STATUS_ERROR;
962 cdr.Stat = Acknowledge;
963 cdr.Result[0] = cdr.StatP;
964 cdr.Result[1] = itob(cdr.ResultTD[2]);
965 cdr.Result[2] = itob(cdr.ResultTD[1]);
966 /* According to Nocash's documentation, the function doesn't care about ff.
967 * This can be seen also in Mednafen's implementation. */
968 //cdr.Result[3] = itob(cdr.ResultTD[0]);
976 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
978 seekTime = cdrSeekTime(cdr.SetSector);
979 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
981 Crusaders of Might and Magic = 0.5x-4x
982 - fix cutscene speech start
988 - fix cutscene speech
993 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
998 case CdlTest + CMD_WHILE_NOT_READY:
999 switch (cdr.Param[0]) {
1000 case 0x20: // System Controller ROM Version
1002 memcpy(cdr.Result, Test20, 4);
1006 memcpy(cdr.Result, Test22, 4);
1008 case 0x23: case 0x24:
1010 memcpy(cdr.Result, Test23, 4);
1016 second_resp_time = 20480;
1019 case CdlID + CMD_PART2:
1021 cdr.Result[0] = cdr.StatP;
1026 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1027 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1028 cdr.Result[1] = 0xc0;
1032 cdr.Result[1] |= 0x10;
1033 if (CdromId[0] == '\0')
1034 cdr.Result[1] |= 0x80;
1036 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1038 /* This adds the string "PCSX" in Playstation bios boot screen */
1039 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1040 cdr.Stat = Complete;
1044 case CdlInit + CMD_WHILE_NOT_READY:
1047 SetPlaySeekRead(cdr.StatP, 0);
1048 // yes, it really sets STATUS_SHELLOPEN
1049 cdr.StatP |= STATUS_SHELLOPEN;
1050 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1056 case CdlGetQ + CMD_WHILE_NOT_READY:
1060 case CdlReadToc + CMD_WHILE_NOT_READY:
1061 second_resp_time = cdReadTime * 180 / 4;
1065 case CdlReadToc + CMD_PART2:
1066 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1067 cdr.Stat = Complete;
1072 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1074 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1075 // Read* acts as play for cdda tracks in cdda mode
1079 if (cdr.SetlocPending) {
1080 seekTime = cdrSeekTime(cdr.SetSector);
1081 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1082 cdr.SetlocPending = 0;
1085 cdr.FirstSector = 1;
1087 // Fighting Force 2 - update subq time immediately
1089 ReadTrack(cdr.SetSectorPlay);
1091 CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
1093 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1098 CDR_LOG_I("Invalid command: %02x%s\n",
1099 Cmd, not_ready ? " (not_ready)" : "");
1100 error = ERROR_INVALIDCMD;
1105 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1106 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1107 cdr.Stat = DiskError;
1111 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1112 printf("cdr.DriveState %d->%d\n", cdr.DriveState, DRIVESTATE_STANDBY);
1113 cdr.DriveState = DRIVESTATE_STANDBY;
1114 cdr.StatP |= STATUS_ROTATING;
1117 if (second_resp_time) {
1118 cdr.CmdInProgress = Cmd | 0x100;
1119 CDR_INT(second_resp_time);
1121 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1122 cdr.CmdInProgress = cdr.Cmd;
1123 CDR_LOG_I("%u cdrom: cmd %02x came before %02x finished\n",
1124 psxRegs.cycle, cdr.Cmd, Cmd);
1131 #define ssat32_to_16(v) \
1132 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1134 #define ssat32_to_16(v) do { \
1135 if (v < -32768) v = -32768; \
1136 else if (v > 32767) v = 32767; \
1140 static void cdrPrepCdda(s16 *buf, int samples)
1142 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1144 for (i = 0; i < samples; i++) {
1145 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1146 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1151 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1154 int ll = cdr.AttenuatorLeftToLeft;
1155 int lr = cdr.AttenuatorLeftToRight;
1156 int rl = cdr.AttenuatorRightToLeft;
1157 int rr = cdr.AttenuatorRightToRight;
1159 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1162 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1166 for (i = 0; i < samples; i++) {
1169 l = (l * ll + r * rl) >> 7;
1170 r = (r * rr + l * lr) >> 7;
1178 for (i = 0; i < samples; i++) {
1180 l = l * (ll + rl) >> 7;
1181 //r = r * (rr + lr) >> 7;
1189 static void cdrReadInterruptSetResult(unsigned char result)
1192 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1193 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1194 cdr.CmdInProgress, cdr.Stat);
1195 cdr.Irq1Pending = result;
1199 cdr.Result[0] = result;
1200 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1204 static void cdrUpdateTransferBuf(const u8 *buf)
1208 memcpy(cdr.Transfer, buf, DATA_SIZE);
1209 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1210 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1211 if (cdr.FifoOffset < 2048 + 12)
1212 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1215 static void cdrReadInterrupt(void)
1217 u8 *buf = NULL, *hdr;
1219 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1221 ReadTrack(cdr.SetSectorPlay);
1223 buf = CDR_getBuffer();
1228 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1229 memset(cdr.Transfer, 0, DATA_SIZE);
1230 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1234 if (!cdr.Irq1Pending)
1235 cdrUpdateTransferBuf(buf);
1237 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1239 // Firemen 2: Multi-XA files - briefings, cutscenes
1240 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1242 cdr.Channel = hdr[1];
1246 * Skips playing on channel 255.
1247 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1248 * TODO : Check if this is the proper behaviour.
1250 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1251 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1253 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1254 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1255 cdr.FirstSector = 0;
1257 else cdr.FirstSector = -1;
1262 Croc 2: $40 - only FORM1 (*)
1263 Judge Dredd: $C8 - only FORM1 (*)
1264 Sim Theme Park - no adpcm at all (zero)
1267 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1268 cdrReadInterruptSetResult(cdr.StatP);
1270 cdr.SetSectorPlay[2]++;
1271 if (cdr.SetSectorPlay[2] == 75) {
1272 cdr.SetSectorPlay[2] = 0;
1273 cdr.SetSectorPlay[1]++;
1274 if (cdr.SetSectorPlay[1] == 60) {
1275 cdr.SetSectorPlay[1] = 0;
1276 cdr.SetSectorPlay[0]++;
1280 if (!cdr.Irq1Pending) {
1281 // update for CdlGetlocP
1282 ReadTrack(cdr.SetSectorPlay);
1285 CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1294 bit 5 - 1 result ready
1296 bit 7 - 1 command being processed
1299 unsigned char cdrRead0(void) {
1300 if (cdr.ResultReady)
1305 cdr.Ctrl |= 0x40; // data fifo not empty
1307 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1310 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1312 return psxHu8(0x1800) = cdr.Ctrl;
1315 void cdrWrite0(unsigned char rt) {
1316 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1318 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1321 unsigned char cdrRead1(void) {
1322 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1323 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1327 if (cdr.ResultP == cdr.ResultC)
1328 cdr.ResultReady = 0;
1330 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1332 return psxHu8(0x1801);
1335 void cdrWrite1(unsigned char rt) {
1336 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1337 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1339 switch (cdr.Ctrl & 3) {
1343 cdr.AttenuatorRightToRightT = rt;
1349 #ifdef CDR_LOG_CMD_IRQ
1350 SysPrintf("%u cdrom: CD1 write: %x (%s)", psxRegs.cycle, rt, CmdName[rt]);
1353 SysPrintf(" Param[%d] = {", cdr.ParamC);
1354 for (i = 0; i < cdr.ParamC; i++)
1355 SysPrintf(" %x,", cdr.Param[i]);
1362 cdr.ResultReady = 0;
1365 if (!cdr.CmdInProgress) {
1366 cdr.CmdInProgress = rt;
1367 // should be something like 12k + controller delays
1371 CDR_LOG_I("%u cdrom: cmd while busy: %02x, prev %02x, busy %02x\n",
1372 psxRegs.cycle, rt, cdr.Cmd, cdr.CmdInProgress);
1373 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1374 cdr.CmdInProgress = rt;
1380 unsigned char cdrRead2(void) {
1381 unsigned char ret = 0;
1383 if (cdr.FifoOffset < cdr.FifoSize)
1384 ret = cdr.Transfer[cdr.FifoOffset++];
1386 CDR_LOG_I("cdrom: read empty fifo (%d)\n", cdr.FifoSize);
1388 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1392 void cdrWrite2(unsigned char rt) {
1393 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1394 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1396 switch (cdr.Ctrl & 3) {
1398 if (cdr.ParamC < 8) // FIXME: size and wrapping
1399 cdr.Param[cdr.ParamC++] = rt;
1406 cdr.AttenuatorLeftToLeftT = rt;
1409 cdr.AttenuatorRightToLeftT = rt;
1414 unsigned char cdrRead3(void) {
1416 psxHu8(0x1803) = cdr.Stat | 0xE0;
1418 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1420 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1421 return psxHu8(0x1803);
1424 void cdrWrite3(unsigned char rt) {
1425 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1426 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1428 switch (cdr.Ctrl & 3) {
1432 if (cdr.Stat & rt) {
1433 #ifdef CDR_LOG_CMD_IRQ
1434 SysPrintf("%u cdrom: ack %02x (w %02x)\n",
1435 psxRegs.cycle, cdr.Stat & rt, rt);
1437 // note: Croc vs Discworld Noir
1438 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) &&
1439 (cdr.CmdInProgress || cdr.Irq1Pending))
1440 CDR_INT(850); // 711-993
1448 cdr.AttenuatorLeftToRightT = rt;
1452 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1453 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1454 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1455 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1461 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1462 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1464 else if (rt & 0x80) {
1465 switch (cdr.Mode & 0x30) {
1466 case MODE_SIZE_2328:
1468 cdr.FifoOffset = 12;
1469 cdr.FifoSize = 2048 + 12;
1472 case MODE_SIZE_2340:
1475 cdr.FifoSize = 2340;
1479 else if (!(rt & 0xc0))
1480 cdr.FifoOffset = DATA_SIZE; // fifo empty
1483 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1488 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1490 switch (chcr & 0x71000000) {
1492 ptr = (u8 *)PSXM(madr);
1494 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1498 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1501 GS CDX: Enhancement CD crash
1504 - Spams DMA3 and gets buffer overrun
1506 size = DATA_SIZE - cdr.FifoOffset;
1511 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1512 cdr.FifoOffset += size;
1513 psxCpu->Clear(madr, size / 4);
1516 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1518 CDRDMA_INT((cdsize/4) * 24);
1520 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1522 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1523 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1527 psxRegs.cycle += (cdsize/4) * 24 - 20;
1532 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1536 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1540 void cdrDmaInterrupt(void)
1542 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1544 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1549 static void getCdInfo(void)
1553 CDR_getTN(cdr.ResultTN);
1554 CDR_getTD(0, cdr.SetSectorEnd);
1555 tmp = cdr.SetSectorEnd[0];
1556 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1557 cdr.SetSectorEnd[2] = tmp;
1561 memset(&cdr, 0, sizeof(cdr));
1567 cdr.DriveState = DRIVESTATE_STANDBY;
1568 cdr.StatP = STATUS_ROTATING;
1569 cdr.FifoOffset = DATA_SIZE; // fifo empty
1571 // BIOS player - default values
1572 cdr.AttenuatorLeftToLeft = 0x80;
1573 cdr.AttenuatorLeftToRight = 0x00;
1574 cdr.AttenuatorRightToLeft = 0x00;
1575 cdr.AttenuatorRightToRight = 0x80;
1580 int cdrFreeze(void *f, int Mode) {
1584 if (Mode == 0 && !Config.Cdda)
1587 cdr.freeze_ver = 0x63647202;
1588 gzfreeze(&cdr, sizeof(cdr));
1591 cdr.ParamP = cdr.ParamC;
1592 tmp = cdr.FifoOffset;
1595 gzfreeze(&tmp, sizeof(tmp));
1600 cdr.FifoOffset = tmp;
1601 cdr.FifoSize = (cdr.Mode & 0x20) ? 2340 : 2048 + 12;
1603 // read right sub data
1604 tmpp[0] = btoi(cdr.Prev[0]);
1605 tmpp[1] = btoi(cdr.Prev[1]);
1606 tmpp[2] = btoi(cdr.Prev[2]);
1611 if (cdr.freeze_ver < 0x63647202)
1612 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1614 Find_CurTrack(cdr.SetSectorPlay);
1616 CDR_play(cdr.SetSectorPlay);
1617 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1618 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1621 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1622 // old versions did not latch Reg2, have to fixup..
1623 if (cdr.Reg2 == 0) {
1624 SysPrintf("cdrom: fixing up old savestate\n");
1627 // also did not save Attenuator..
1628 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1629 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1631 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1639 void LidInterrupt(void) {
1641 cdrLidSeekInterrupt();