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(...)
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;
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;
112 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
113 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
114 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
115 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
117 static s16 read_buf[CD_FRAMESIZE_RAW/2];
119 /* CD-ROM magic numbers */
120 #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
125 #define CdlBackward 5
133 #define CdlSetfilter 13
134 #define CdlSetmode 14
135 #define CdlGetparam 15
136 #define CdlGetlocL 16
137 #define CdlGetlocP 17
143 #define CdlSetclock 23
144 #define CdlGetclock 24
150 #define CdlReadToc 30
152 #ifdef CDR_LOG_CMD_IRQ
153 static const char * const CmdName[0x100] = {
154 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
155 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
156 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
157 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
158 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
159 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
160 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
161 "CdlInit", NULL, "CDlReadToc", NULL
165 unsigned char Test04[] = { 0 };
166 unsigned char Test05[] = { 0 };
167 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
168 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
169 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
175 #define Acknowledge 3
180 #define MODE_SPEED (1<<7) // 0x80
181 #define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
182 #define MODE_SIZE_2340 (1<<5) // 0x20
183 #define MODE_SIZE_2328 (1<<4) // 0x10
184 #define MODE_SIZE_2048 (0<<4) // 0x00
185 #define MODE_SF (1<<3) // 0x08 channel on/off
186 #define MODE_REPORT (1<<2) // 0x04
187 #define MODE_AUTOPAUSE (1<<1) // 0x02
188 #define MODE_CDDA (1<<0) // 0x01
191 #define STATUS_PLAY (1<<7) // 0x80
192 #define STATUS_SEEK (1<<6) // 0x40
193 #define STATUS_READ (1<<5) // 0x20
194 #define STATUS_SHELLOPEN (1<<4) // 0x10
195 #define STATUS_UNKNOWN3 (1<<3) // 0x08
196 #define STATUS_UNKNOWN2 (1<<2) // 0x04
197 #define STATUS_ROTATING (1<<1) // 0x02
198 #define STATUS_ERROR (1<<0) // 0x01
201 #define ERROR_NOTREADY (1<<7) // 0x80
202 #define ERROR_INVALIDCMD (1<<6) // 0x40
203 #define ERROR_INVALIDARG (1<<5) // 0x20
205 // 1x = 75 sectors per second
206 // PSXCLK = 1 sec in the ps
207 // so (PSXCLK / 75) = cdr read time (linuzappz)
208 #define cdReadTime (PSXCLK / 75)
211 DRIVESTATE_STANDBY = 0, // pause, play, read
213 DRIVESTATE_RESCAN_CD,
214 DRIVESTATE_PREPARE_CD,
218 static struct CdrStat stat;
220 static unsigned int msf2sec(const u8 *msf) {
221 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
224 // for that weird psemu API..
225 static unsigned int fsm2sec(const u8 *msf) {
226 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
229 static void sec2msf(unsigned int s, u8 *msf) {
230 msf[0] = s / 75 / 60;
231 s = s - msf[0] * 75 * 60;
238 #define CDR_INT(eCycle) { \
239 psxRegs.interrupt |= (1 << PSXINT_CDR); \
240 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
241 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
242 new_dyna_set_event(PSXINT_CDR, eCycle); \
245 // cdrPlaySeekReadInterrupt
246 #define CDRPLAYSEEKREAD_INT(eCycle, isFirst) { \
248 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
250 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
252 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
253 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
254 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
257 // cdrLidSeekInterrupt
258 #define CDRLID_INT(eCycle) { \
259 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
260 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
261 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
262 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
265 #define StopReading() { \
267 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
270 #define StopCdda() { \
271 if (cdr.Play && !Config.Cdda) CDR_stop(); \
273 cdr.FastForward = 0; \
274 cdr.FastBackward = 0; \
277 #define SetPlaySeekRead(x, f) { \
278 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
282 #define SetResultSize(size) { \
284 cdr.ResultC = size; \
285 cdr.ResultReady = 1; \
288 static void setIrq(int log_cmd)
290 if (cdr.Stat & cdr.Reg2)
291 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
293 #ifdef CDR_LOG_CMD_IRQ
297 SysPrintf("CDR IRQ=%d cmd %02x stat %02x: ",
298 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
299 for (i = 0; i < cdr.ResultC; i++)
300 SysPrintf("%02x ", cdr.Result[i]);
306 // timing used in this function was taken from tests on real hardware
307 // (yes it's slow, but you probably don't want to modify it)
308 void cdrLidSeekInterrupt(void)
310 switch (cdr.DriveState) {
312 case DRIVESTATE_STANDBY:
315 SetPlaySeekRead(cdr.StatP, 0);
317 if (CDR_getStatus(&stat) == -1)
320 if (stat.Status & STATUS_SHELLOPEN)
322 cdr.DriveState = DRIVESTATE_LID_OPEN;
327 case DRIVESTATE_LID_OPEN:
328 if (CDR_getStatus(&stat) == -1)
329 stat.Status &= ~STATUS_SHELLOPEN;
332 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
333 cdr.StatP |= STATUS_SHELLOPEN;
335 // could generate error irq here, but real hardware
336 // only sometimes does that
337 // (not done when lots of commands are sent?)
339 CDRLID_INT(cdReadTime * 30);
342 else if (cdr.StatP & STATUS_ROTATING) {
343 cdr.StatP &= ~STATUS_ROTATING;
345 else if (!(stat.Status & STATUS_SHELLOPEN)) {
349 // cdr.StatP STATUS_SHELLOPEN is "sticky"
350 // and is only cleared by CdlNop
352 cdr.DriveState = DRIVESTATE_RESCAN_CD;
353 CDRLID_INT(cdReadTime * 105);
358 CDRLID_INT(cdReadTime * 3);
361 case DRIVESTATE_RESCAN_CD:
362 cdr.StatP |= STATUS_ROTATING;
363 cdr.DriveState = DRIVESTATE_PREPARE_CD;
365 // this is very long on real hardware, over 6 seconds
366 // make it a bit faster here...
367 CDRLID_INT(cdReadTime * 150);
370 case DRIVESTATE_PREPARE_CD:
371 cdr.StatP |= STATUS_SEEK;
373 cdr.DriveState = DRIVESTATE_STANDBY;
374 CDRLID_INT(cdReadTime * 26);
379 static void Find_CurTrack(const u8 *time)
383 current = msf2sec(time);
385 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
386 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
387 sect = fsm2sec(cdr.ResultTD);
388 if (sect - current >= 150)
393 static void generate_subq(const u8 *time)
395 unsigned char start[3], next[3];
396 unsigned int this_s, start_s, next_s, pregap;
399 CDR_getTD(cdr.CurTrack, start);
400 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
402 CDR_getTD(cdr.CurTrack + 1, next);
405 // last track - cd size
407 next[0] = cdr.SetSectorEnd[2];
408 next[1] = cdr.SetSectorEnd[1];
409 next[2] = cdr.SetSectorEnd[0];
412 this_s = msf2sec(time);
413 start_s = fsm2sec(start);
414 next_s = fsm2sec(next);
416 cdr.TrackChanged = FALSE;
418 if (next_s - this_s < pregap) {
419 cdr.TrackChanged = TRUE;
426 relative_s = this_s - start_s;
427 if (relative_s < 0) {
429 relative_s = -relative_s;
431 sec2msf(relative_s, cdr.subq.Relative);
433 cdr.subq.Track = itob(cdr.CurTrack);
434 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
435 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
436 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
437 cdr.subq.Absolute[0] = itob(time[0]);
438 cdr.subq.Absolute[1] = itob(time[1]);
439 cdr.subq.Absolute[2] = itob(time[2]);
442 static void ReadTrack(const u8 *time) {
443 unsigned char tmp[3];
447 tmp[0] = itob(time[0]);
448 tmp[1] = itob(time[1]);
449 tmp[2] = itob(time[2]);
451 if (memcmp(cdr.Prev, tmp, 3) == 0)
454 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
456 cdr.NoErr = CDR_readTrack(tmp);
457 memcpy(cdr.Prev, tmp, 3);
462 subq = (struct SubQ *)CDR_getBufferSub();
463 if (subq != NULL && cdr.CurTrack == 1) {
464 crc = calcCrc((u8 *)subq + 12, 10);
465 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
466 cdr.subq.Track = subq->TrackNumber;
467 cdr.subq.Index = subq->IndexNumber;
468 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
469 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
472 CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
473 tmp[0], tmp[1], tmp[2]);
480 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
481 cdr.subq.Track, cdr.subq.Index,
482 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
483 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
486 static void cdrPlayInterrupt_Autopause()
489 boolean abs_lev_chselect;
492 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
493 CDR_LOG( "CDDA STOP\n" );
495 // Magic the Gathering
496 // - looping territory cdda
499 //cdr.ResultReady = 1;
500 //cdr.Stat = DataReady;
505 SetPlaySeekRead(cdr.StatP, 0);
507 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
508 cdr.Result[0] = cdr.StatP;
509 cdr.Result[1] = cdr.subq.Track;
510 cdr.Result[2] = cdr.subq.Index;
512 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
514 /* 8 is a hack. For accuracy, it should be 588. */
515 for (i = 0; i < 8; i++)
517 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
519 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
520 abs_lev_max |= abs_lev_chselect << 15;
522 if (cdr.subq.Absolute[2] & 0x10) {
523 cdr.Result[3] = cdr.subq.Relative[0];
524 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
525 cdr.Result[5] = cdr.subq.Relative[2];
528 cdr.Result[3] = cdr.subq.Absolute[0];
529 cdr.Result[4] = cdr.subq.Absolute[1];
530 cdr.Result[5] = cdr.subq.Absolute[2];
533 cdr.Result[6] = abs_lev_max >> 0;
534 cdr.Result[7] = abs_lev_max >> 8;
536 // Rayman: Logo freeze (resultready + dataready)
538 cdr.Stat = DataReady;
545 static int cdrSeekTime(unsigned char *target)
547 int seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
550 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
551 * and was unreliable for that game.
552 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
554 * Obviously, this isn't perfect but right now, it should be a bit better.
555 * Games to test this against if you change that setting :
556 * - Driver (titlescreen music delay and retry mission)
557 * - Worms Pinball (Will either not boot or crash in the memory card screen)
558 * - Viewpoint (short pauses if the delay in the ingame music is too long)
560 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
561 * However, 1000000 is not enough for Worms Pinball to reliably boot.
563 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
564 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
568 static void cdrReadInterrupt(void);
570 void cdrPlaySeekReadInterrupt(void)
577 if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
579 CDR_LOG_I("cdrom: seek stat hack\n");
580 CDRPLAYSEEKREAD_INT(0x1000, 1);
584 cdr.StatP |= STATUS_ROTATING;
585 SetPlaySeekRead(cdr.StatP, 0);
586 cdr.Result[0] = cdr.StatP;
590 Find_CurTrack(cdr.SetSectorPlay);
591 ReadTrack(cdr.SetSectorPlay);
592 cdr.TrackChanged = FALSE;
596 if (!cdr.Play) return;
598 CDR_LOG( "CDDA - %d:%d:%d\n",
599 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
601 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
602 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
604 SetPlaySeekRead(cdr.StatP, 0);
605 cdr.TrackChanged = TRUE;
608 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
611 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
612 cdrPlayInterrupt_Autopause();
614 if (!cdr.Muted && !Config.Cdda) {
615 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
616 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
620 cdr.SetSectorPlay[2]++;
621 if (cdr.SetSectorPlay[2] == 75) {
622 cdr.SetSectorPlay[2] = 0;
623 cdr.SetSectorPlay[1]++;
624 if (cdr.SetSectorPlay[1] == 60) {
625 cdr.SetSectorPlay[1] = 0;
626 cdr.SetSectorPlay[0]++;
630 // update for CdlGetlocP/autopause
631 generate_subq(cdr.SetSectorPlay);
633 CDRPLAYSEEKREAD_INT(cdReadTime, 0);
636 void cdrInterrupt(void) {
637 int no_busy_error = 0;
638 int start_rotating = 0;
640 unsigned int seekTime = 0;
641 u32 second_resp_time = 0;
648 CDR_LOG_I("cdrom: cmd %02x with irqstat %x\n", cdr.CmdInProgress, cdr.Stat);
656 cdr.Result[0] = cdr.StatP;
657 cdr.Stat = Acknowledge;
659 Cmd = cdr.CmdInProgress;
660 cdr.CmdInProgress = 0;
670 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
671 cdr.StatP &= ~STATUS_SHELLOPEN;
676 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
678 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
679 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))
681 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
682 error = ERROR_INVALIDARG;
687 for (i = 0; i < 3; i++)
688 set_loc[i] = btoi(cdr.Param[i]);
689 memcpy(cdr.SetSector, set_loc, 3);
690 cdr.SetSector[3] = 0;
691 cdr.SetlocPending = 1;
700 cdr.FastBackward = 0;
704 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
706 if (ParamC != 0 && cdr.Param[0] != 0) {
707 int track = btoi( cdr.Param[0] );
709 if (track <= cdr.ResultTN[1])
710 cdr.CurTrack = track;
712 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
714 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
715 for (i = 0; i < 3; i++)
716 set_loc[i] = cdr.ResultTD[2 - i];
717 seekTime = cdrSeekTime(set_loc);
718 memcpy(cdr.SetSectorPlay, set_loc, 3);
721 else if (cdr.SetlocPending) {
722 seekTime = cdrSeekTime(cdr.SetSector);
723 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
726 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
727 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
729 cdr.SetlocPending = 0;
732 Rayman: detect track changes
735 Twisted Metal 2: skip PREGAP + starting accurate SubQ
736 - plays tracks without retry play
738 Wild 9: skip PREGAP + starting accurate SubQ
739 - plays tracks without retry play
741 Find_CurTrack(cdr.SetSectorPlay);
742 ReadTrack(cdr.SetSectorPlay);
743 cdr.TrackChanged = FALSE;
747 CDR_play(cdr.SetSectorPlay);
749 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
751 // BIOS player - set flag again
754 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
759 // TODO: error 80 if stopped
762 // GameShark CD Player: Calls 2x + Play 2x
764 cdr.FastBackward = 0;
770 // GameShark CD Player: Calls 2x + Play 2x
771 cdr.FastBackward = 1;
776 if (cdr.DriveState != DRIVESTATE_STOPPED) {
777 error = ERROR_INVALIDARG;
780 second_resp_time = cdReadTime * 125 / 2;
784 case CdlStandby + 0x100:
790 // grab time for current track
791 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
793 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
794 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
795 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
800 SetPlaySeekRead(cdr.StatP, 0);
801 cdr.StatP &= ~STATUS_ROTATING;
803 second_resp_time = 0x800;
804 if (cdr.DriveState == DRIVESTATE_STANDBY)
805 second_resp_time = cdReadTime * 30 / 2;
807 cdr.DriveState = DRIVESTATE_STOPPED;
810 case CdlStop + 0x100:
818 Gundam Battle Assault 2: much slower (*)
819 - Fixes boot, gameplay
821 Hokuto no Ken 2: slower
822 - Fixes intro + subtitles
824 InuYasha - Feudal Fairy Tale: slower
827 /* Gameblabla - Tightening the timings (as taken from Duckstation).
828 * The timings from Duckstation are based upon hardware tests.
829 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
830 * seems to be timing sensitive as it can depend on the CPU's clock speed.
832 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
834 second_resp_time = 7000;
838 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
840 SetPlaySeekRead(cdr.StatP, 0);
844 case CdlPause + 0x100:
851 SetPlaySeekRead(cdr.StatP, 0);
853 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
854 second_resp_time = 4100000;
859 case CdlReset + 0x100:
872 cdr.File = cdr.Param[0];
873 cdr.Channel = cdr.Param[1];
877 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
878 cdr.Mode = cdr.Param[0];
883 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
885 cdr.Result[1] = cdr.Mode;
887 cdr.Result[3] = cdr.File;
888 cdr.Result[4] = cdr.Channel;
894 memcpy(cdr.Result, cdr.Transfer, 8);
899 memcpy(&cdr.Result, &cdr.subq, 8);
902 case CdlReadT: // SetSession?
904 second_resp_time = cdReadTime * 290 / 4;
908 case CdlReadT + 0x100:
914 if (CDR_getTN(cdr.ResultTN) == -1) {
915 cdr.Stat = DiskError;
916 cdr.Result[0] |= STATUS_ERROR;
918 cdr.Stat = Acknowledge;
919 cdr.Result[1] = itob(cdr.ResultTN[0]);
920 cdr.Result[2] = itob(cdr.ResultTN[1]);
925 cdr.Track = btoi(cdr.Param[0]);
927 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
928 cdr.Stat = DiskError;
929 cdr.Result[0] |= STATUS_ERROR;
931 cdr.Stat = Acknowledge;
932 cdr.Result[0] = cdr.StatP;
933 cdr.Result[1] = itob(cdr.ResultTD[2]);
934 cdr.Result[2] = itob(cdr.ResultTD[1]);
935 /* According to Nocash's documentation, the function doesn't care about ff.
936 * This can be seen also in Mednafen's implementation. */
937 //cdr.Result[3] = itob(cdr.ResultTD[0]);
945 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
947 seekTime = cdrSeekTime(cdr.SetSector);
948 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
950 Crusaders of Might and Magic = 0.5x-4x
951 - fix cutscene speech start
957 - fix cutscene speech
962 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
967 switch (cdr.Param[0]) {
968 case 0x20: // System Controller ROM Version
970 memcpy(cdr.Result, Test20, 4);
974 memcpy(cdr.Result, Test22, 4);
976 case 0x23: case 0x24:
978 memcpy(cdr.Result, Test23, 4);
985 second_resp_time = 20480;
990 cdr.Result[0] = cdr.StatP;
995 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
996 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
997 cdr.Result[1] = 0xc0;
1001 cdr.Result[1] |= 0x10;
1002 if (CdromId[0] == '\0')
1003 cdr.Result[1] |= 0x80;
1005 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1007 /* This adds the string "PCSX" in Playstation bios boot screen */
1008 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1009 cdr.Stat = Complete;
1015 SetPlaySeekRead(cdr.StatP, 0);
1016 // yes, it really sets STATUS_SHELLOPEN
1017 cdr.StatP |= STATUS_SHELLOPEN;
1018 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1029 second_resp_time = cdReadTime * 180 / 4;
1034 case CdlReadToc + 0x100:
1035 cdr.Stat = Complete;
1041 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1043 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1044 // Read* acts as play for cdda tracks in cdda mode
1048 if (cdr.SetlocPending) {
1049 seekTime = cdrSeekTime(cdr.SetSector);
1050 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1051 cdr.SetlocPending = 0;
1054 cdr.FirstSector = 1;
1056 // Fighting Force 2 - update subq time immediately
1058 ReadTrack(cdr.SetSectorPlay);
1060 CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
1062 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1067 CDR_LOG_I("Invalid command: %02x\n", Cmd);
1068 error = ERROR_INVALIDCMD;
1073 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1074 cdr.Result[1] = error;
1075 cdr.Stat = DiskError;
1079 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1080 cdr.DriveState = DRIVESTATE_STANDBY;
1081 cdr.StatP |= STATUS_ROTATING;
1084 if (!no_busy_error) {
1085 switch (cdr.DriveState) {
1086 case DRIVESTATE_LID_OPEN:
1087 case DRIVESTATE_RESCAN_CD:
1088 case DRIVESTATE_PREPARE_CD:
1090 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1091 cdr.Result[1] = ERROR_NOTREADY;
1092 cdr.Stat = DiskError;
1097 if (second_resp_time) {
1098 cdr.CmdInProgress = Cmd | 0x100;
1099 CDR_INT(second_resp_time);
1101 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1102 cdr.CmdInProgress = cdr.Cmd;
1103 CDR_LOG_I("cdrom: cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1110 #define ssat32_to_16(v) \
1111 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1113 #define ssat32_to_16(v) do { \
1114 if (v < -32768) v = -32768; \
1115 else if (v > 32767) v = 32767; \
1119 void cdrAttenuate(s16 *buf, int samples, int stereo)
1122 int ll = cdr.AttenuatorLeftToLeft;
1123 int lr = cdr.AttenuatorLeftToRight;
1124 int rl = cdr.AttenuatorRightToLeft;
1125 int rr = cdr.AttenuatorRightToRight;
1127 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1130 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1134 for (i = 0; i < samples; i++) {
1137 l = (l * ll + r * rl) >> 7;
1138 r = (r * rr + l * lr) >> 7;
1146 for (i = 0; i < samples; i++) {
1148 l = l * (ll + rl) >> 7;
1149 //r = r * (rr + lr) >> 7;
1157 static void cdrReadInterruptSetResult(unsigned char result)
1160 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1161 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1162 cdr.CmdInProgress, cdr.Stat);
1163 cdr.Irq1Pending = result;
1167 cdr.Result[0] = result;
1168 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1172 static void cdrUpdateTransferBuf(const u8 *buf)
1176 memcpy(cdr.Transfer, buf, DATA_SIZE);
1177 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1178 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1182 static void cdrReadInterrupt(void)
1184 u8 *buf = NULL, *hdr;
1186 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1188 ReadTrack(cdr.SetSectorPlay);
1190 buf = CDR_getBuffer();
1195 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1196 memset(cdr.Transfer, 0, DATA_SIZE);
1197 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1201 if (!cdr.Irq1Pending)
1202 cdrUpdateTransferBuf(buf);
1204 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1206 // Firemen 2: Multi-XA files - briefings, cutscenes
1207 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1209 cdr.Channel = hdr[1];
1213 * Skips playing on channel 255.
1214 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1215 * TODO : Check if this is the proper behaviour.
1217 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1218 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1220 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1221 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1222 cdr.FirstSector = 0;
1224 else cdr.FirstSector = -1;
1229 Croc 2: $40 - only FORM1 (*)
1230 Judge Dredd: $C8 - only FORM1 (*)
1231 Sim Theme Park - no adpcm at all (zero)
1234 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1235 cdrReadInterruptSetResult(cdr.StatP);
1237 cdr.SetSectorPlay[2]++;
1238 if (cdr.SetSectorPlay[2] == 75) {
1239 cdr.SetSectorPlay[2] = 0;
1240 cdr.SetSectorPlay[1]++;
1241 if (cdr.SetSectorPlay[1] == 60) {
1242 cdr.SetSectorPlay[1] = 0;
1243 cdr.SetSectorPlay[0]++;
1247 if (!cdr.Irq1Pending) {
1248 // update for CdlGetlocP
1249 ReadTrack(cdr.SetSectorPlay);
1252 CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1255 static void doMissedIrqs(void)
1257 if (cdr.Irq1Pending)
1259 // hand out the "newest" sector, according to nocash
1260 cdrUpdateTransferBuf(CDR_getBuffer());
1261 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
1262 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1264 cdr.Result[0] = cdr.Irq1Pending;
1265 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
1266 cdr.Irq1Pending = 0;
1270 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) && cdr.CmdInProgress)
1280 bit 5 - 1 result ready
1282 bit 7 - 1 command being processed
1285 unsigned char cdrRead0(void) {
1286 if (cdr.ResultReady)
1291 cdr.Ctrl |= 0x40; // data fifo not empty
1293 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1296 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1298 return psxHu8(0x1800) = cdr.Ctrl;
1301 void cdrWrite0(unsigned char rt) {
1302 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1304 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1307 unsigned char cdrRead1(void) {
1308 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1309 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1313 if (cdr.ResultP == cdr.ResultC)
1314 cdr.ResultReady = 0;
1316 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1318 return psxHu8(0x1801);
1321 void cdrWrite1(unsigned char rt) {
1322 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1323 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1325 switch (cdr.Ctrl & 3) {
1329 cdr.AttenuatorRightToRightT = rt;
1335 #ifdef CDR_LOG_CMD_IRQ
1336 SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1339 SysPrintf(" Param[%d] = {", cdr.ParamC);
1340 for (i = 0; i < cdr.ParamC; i++)
1341 SysPrintf(" %x,", cdr.Param[i]);
1348 cdr.ResultReady = 0;
1351 if (!cdr.CmdInProgress) {
1352 cdr.CmdInProgress = rt;
1353 // should be something like 12k + controller delays
1357 CDR_LOG_I("cdr: cmd while busy: %02x, prev %02x, busy %02x\n",
1358 rt, cdr.Cmd, cdr.CmdInProgress);
1359 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1360 cdr.CmdInProgress = rt;
1366 unsigned char cdrRead2(void) {
1367 unsigned char ret = 0;
1369 if (cdr.Readed && cdr.FifoOffset < DATA_SIZE)
1370 ret = cdr.Transfer[cdr.FifoOffset++];
1372 CDR_LOG_I("cdrom: read empty fifo\n");
1374 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1378 void cdrWrite2(unsigned char rt) {
1379 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1380 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1382 switch (cdr.Ctrl & 3) {
1384 if (cdr.ParamC < 8) // FIXME: size and wrapping
1385 cdr.Param[cdr.ParamC++] = rt;
1392 cdr.AttenuatorLeftToLeftT = rt;
1395 cdr.AttenuatorRightToLeftT = rt;
1400 unsigned char cdrRead3(void) {
1402 psxHu8(0x1803) = cdr.Stat | 0xE0;
1404 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1406 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1407 return psxHu8(0x1803);
1410 void cdrWrite3(unsigned char rt) {
1411 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1412 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1414 switch (cdr.Ctrl & 3) {
1418 #ifdef CDR_LOG_CMD_IRQ
1420 SysPrintf("ack %02x\n", cdr.Stat & rt);
1429 cdr.AttenuatorLeftToRightT = rt;
1433 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1434 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1435 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1436 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1441 if ((rt & 0x80) && cdr.Readed == 0) {
1444 switch (cdr.Mode & 0x30) {
1445 case MODE_SIZE_2328:
1447 cdr.FifoOffset = 12;
1450 case MODE_SIZE_2340:
1458 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1463 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1465 switch (chcr & 0x71000000) {
1467 if (cdr.Readed == 0) {
1468 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1471 ptr = (u8 *)PSXM(madr);
1472 if (ptr == INVALID_PTR) {
1473 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1477 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1480 GS CDX: Enhancement CD crash
1483 - Spams DMA3 and gets buffer overrun
1485 size = DATA_SIZE - cdr.FifoOffset;
1490 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1491 cdr.FifoOffset += size;
1492 psxCpu->Clear(madr, size / 4);
1495 CDRDMA_INT((cdsize/4) * 24);
1497 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1499 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1500 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1504 psxRegs.cycle += (cdsize/4) * 24 - 20;
1509 CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1513 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1517 void cdrDmaInterrupt(void)
1519 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1521 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1526 static void getCdInfo(void)
1530 CDR_getTN(cdr.ResultTN);
1531 CDR_getTD(0, cdr.SetSectorEnd);
1532 tmp = cdr.SetSectorEnd[0];
1533 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1534 cdr.SetSectorEnd[2] = tmp;
1538 memset(&cdr, 0, sizeof(cdr));
1544 cdr.DriveState = DRIVESTATE_STANDBY;
1545 cdr.StatP = STATUS_ROTATING;
1546 cdr.FifoOffset = DATA_SIZE; // fifo empty
1548 // BIOS player - default values
1549 cdr.AttenuatorLeftToLeft = 0x80;
1550 cdr.AttenuatorLeftToRight = 0x00;
1551 cdr.AttenuatorRightToLeft = 0x00;
1552 cdr.AttenuatorRightToRight = 0x80;
1557 int cdrFreeze(void *f, int Mode) {
1561 if (Mode == 0 && !Config.Cdda)
1564 cdr.freeze_ver = 0x63647202;
1565 gzfreeze(&cdr, sizeof(cdr));
1568 cdr.ParamP = cdr.ParamC;
1569 tmp = cdr.FifoOffset;
1572 gzfreeze(&tmp, sizeof(tmp));
1577 cdr.FifoOffset = tmp;
1579 // read right sub data
1580 tmpp[0] = btoi(cdr.Prev[0]);
1581 tmpp[1] = btoi(cdr.Prev[1]);
1582 tmpp[2] = btoi(cdr.Prev[2]);
1587 if (cdr.freeze_ver < 0x63647202)
1588 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1590 Find_CurTrack(cdr.SetSectorPlay);
1592 CDR_play(cdr.SetSectorPlay);
1593 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1594 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1597 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1598 // old versions did not latch Reg2, have to fixup..
1599 if (cdr.Reg2 == 0) {
1600 SysPrintf("cdrom: fixing up old savestate\n");
1603 // also did not save Attenuator..
1604 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1605 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1607 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1615 void LidInterrupt(void) {
1617 cdrLidSeekInterrupt();