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("CDR IRQ=%d cmd %02x stat %02x: ",
299 !!(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;
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 seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
551 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
552 * and was unreliable for that game.
553 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
555 * Obviously, this isn't perfect but right now, it should be a bit better.
556 * Games to test this against if you change that setting :
557 * - Driver (titlescreen music delay and retry mission)
558 * - Worms Pinball (Will either not boot or crash in the memory card screen)
559 * - Viewpoint (short pauses if the delay in the ingame music is too long)
561 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
562 * However, 1000000 is not enough for Worms Pinball to reliably boot.
564 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
565 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
569 static void cdrUpdateTransferBuf(const u8 *buf);
570 static void cdrReadInterrupt(void);
571 static void cdrPrepCdda(s16 *buf, int samples);
572 static void cdrAttenuate(s16 *buf, int samples, int stereo);
574 void cdrPlaySeekReadInterrupt(void)
581 if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
583 CDR_LOG_I("cdrom: seek stat hack\n");
584 CDRPLAYSEEKREAD_INT(0x1000, 1);
588 cdr.StatP |= STATUS_ROTATING;
589 SetPlaySeekRead(cdr.StatP, 0);
590 cdr.Result[0] = cdr.StatP;
594 Find_CurTrack(cdr.SetSectorPlay);
595 ReadTrack(cdr.SetSectorPlay);
596 cdr.TrackChanged = FALSE;
600 if (!cdr.Play) return;
602 CDR_LOG( "CDDA - %d:%d:%d\n",
603 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
605 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
606 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
608 SetPlaySeekRead(cdr.StatP, 0);
609 cdr.TrackChanged = TRUE;
612 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
615 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
616 cdrPlayInterrupt_Autopause();
618 if (!cdr.Muted && !Config.Cdda) {
619 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
620 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
621 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
625 cdr.SetSectorPlay[2]++;
626 if (cdr.SetSectorPlay[2] == 75) {
627 cdr.SetSectorPlay[2] = 0;
628 cdr.SetSectorPlay[1]++;
629 if (cdr.SetSectorPlay[1] == 60) {
630 cdr.SetSectorPlay[1] = 0;
631 cdr.SetSectorPlay[0]++;
635 // update for CdlGetlocP/autopause
636 generate_subq(cdr.SetSectorPlay);
638 CDRPLAYSEEKREAD_INT(cdReadTime, 0);
641 void cdrInterrupt(void) {
642 int no_busy_error = 0;
643 int start_rotating = 0;
645 unsigned int seekTime = 0;
646 u32 second_resp_time = 0;
653 CDR_LOG_I("cdrom: cmd %02x with irqstat %x\n", cdr.CmdInProgress, cdr.Stat);
656 if (cdr.Irq1Pending) {
657 // hand out the "newest" sector, according to nocash
658 cdrUpdateTransferBuf(CDR_getBuffer());
659 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
660 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
662 cdr.Result[0] = cdr.Irq1Pending;
663 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
673 cdr.Result[0] = cdr.StatP;
674 cdr.Stat = Acknowledge;
676 Cmd = cdr.CmdInProgress;
677 cdr.CmdInProgress = 0;
687 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
688 cdr.StatP &= ~STATUS_SHELLOPEN;
693 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
695 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
696 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))
698 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
699 error = ERROR_INVALIDARG;
704 for (i = 0; i < 3; i++)
705 set_loc[i] = btoi(cdr.Param[i]);
706 memcpy(cdr.SetSector, set_loc, 3);
707 cdr.SetSector[3] = 0;
708 cdr.SetlocPending = 1;
717 cdr.FastBackward = 0;
721 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
723 if (ParamC != 0 && cdr.Param[0] != 0) {
724 int track = btoi( cdr.Param[0] );
726 if (track <= cdr.ResultTN[1])
727 cdr.CurTrack = track;
729 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
731 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
732 for (i = 0; i < 3; i++)
733 set_loc[i] = cdr.ResultTD[2 - i];
734 seekTime = cdrSeekTime(set_loc);
735 memcpy(cdr.SetSectorPlay, set_loc, 3);
738 else if (cdr.SetlocPending) {
739 seekTime = cdrSeekTime(cdr.SetSector);
740 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
743 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
744 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
746 cdr.SetlocPending = 0;
749 Rayman: detect track changes
752 Twisted Metal 2: skip PREGAP + starting accurate SubQ
753 - plays tracks without retry play
755 Wild 9: skip PREGAP + starting accurate SubQ
756 - plays tracks without retry play
758 Find_CurTrack(cdr.SetSectorPlay);
759 ReadTrack(cdr.SetSectorPlay);
760 cdr.TrackChanged = FALSE;
764 CDR_play(cdr.SetSectorPlay);
766 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
768 // BIOS player - set flag again
771 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
776 // TODO: error 80 if stopped
779 // GameShark CD Player: Calls 2x + Play 2x
781 cdr.FastBackward = 0;
787 // GameShark CD Player: Calls 2x + Play 2x
788 cdr.FastBackward = 1;
793 if (cdr.DriveState != DRIVESTATE_STOPPED) {
794 error = ERROR_INVALIDARG;
797 second_resp_time = cdReadTime * 125 / 2;
801 case CdlStandby + 0x100:
807 // grab time for current track
808 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
810 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
811 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
812 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
817 SetPlaySeekRead(cdr.StatP, 0);
818 cdr.StatP &= ~STATUS_ROTATING;
820 second_resp_time = 0x800;
821 if (cdr.DriveState == DRIVESTATE_STANDBY)
822 second_resp_time = cdReadTime * 30 / 2;
824 cdr.DriveState = DRIVESTATE_STOPPED;
827 case CdlStop + 0x100:
835 Gundam Battle Assault 2: much slower (*)
836 - Fixes boot, gameplay
838 Hokuto no Ken 2: slower
839 - Fixes intro + subtitles
841 InuYasha - Feudal Fairy Tale: slower
844 /* Gameblabla - Tightening the timings (as taken from Duckstation).
845 * The timings from Duckstation are based upon hardware tests.
846 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
847 * seems to be timing sensitive as it can depend on the CPU's clock speed.
849 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
851 second_resp_time = 7000;
855 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
857 SetPlaySeekRead(cdr.StatP, 0);
861 case CdlPause + 0x100:
868 SetPlaySeekRead(cdr.StatP, 0);
870 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
871 second_resp_time = 4100000;
876 case CdlReset + 0x100:
889 cdr.File = cdr.Param[0];
890 cdr.Channel = cdr.Param[1];
894 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
895 cdr.Mode = cdr.Param[0];
900 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
902 cdr.Result[1] = cdr.Mode;
904 cdr.Result[3] = cdr.File;
905 cdr.Result[4] = cdr.Channel;
911 memcpy(cdr.Result, cdr.Transfer, 8);
916 memcpy(&cdr.Result, &cdr.subq, 8);
919 case CdlReadT: // SetSession?
921 second_resp_time = cdReadTime * 290 / 4;
925 case CdlReadT + 0x100:
931 if (CDR_getTN(cdr.ResultTN) == -1) {
932 cdr.Stat = DiskError;
933 cdr.Result[0] |= STATUS_ERROR;
935 cdr.Stat = Acknowledge;
936 cdr.Result[1] = itob(cdr.ResultTN[0]);
937 cdr.Result[2] = itob(cdr.ResultTN[1]);
942 cdr.Track = btoi(cdr.Param[0]);
944 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
945 cdr.Stat = DiskError;
946 cdr.Result[0] |= STATUS_ERROR;
948 cdr.Stat = Acknowledge;
949 cdr.Result[0] = cdr.StatP;
950 cdr.Result[1] = itob(cdr.ResultTD[2]);
951 cdr.Result[2] = itob(cdr.ResultTD[1]);
952 /* According to Nocash's documentation, the function doesn't care about ff.
953 * This can be seen also in Mednafen's implementation. */
954 //cdr.Result[3] = itob(cdr.ResultTD[0]);
962 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
964 seekTime = cdrSeekTime(cdr.SetSector);
965 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
967 Crusaders of Might and Magic = 0.5x-4x
968 - fix cutscene speech start
974 - fix cutscene speech
979 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
984 switch (cdr.Param[0]) {
985 case 0x20: // System Controller ROM Version
987 memcpy(cdr.Result, Test20, 4);
991 memcpy(cdr.Result, Test22, 4);
993 case 0x23: case 0x24:
995 memcpy(cdr.Result, Test23, 4);
1002 second_resp_time = 20480;
1007 cdr.Result[0] = cdr.StatP;
1012 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1013 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1014 cdr.Result[1] = 0xc0;
1018 cdr.Result[1] |= 0x10;
1019 if (CdromId[0] == '\0')
1020 cdr.Result[1] |= 0x80;
1022 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1024 /* This adds the string "PCSX" in Playstation bios boot screen */
1025 memcpy((char *)&cdr.Result[4], "PCSX", 4);
1026 cdr.Stat = Complete;
1032 SetPlaySeekRead(cdr.StatP, 0);
1033 // yes, it really sets STATUS_SHELLOPEN
1034 cdr.StatP |= STATUS_SHELLOPEN;
1035 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1046 second_resp_time = cdReadTime * 180 / 4;
1051 case CdlReadToc + 0x100:
1052 cdr.Stat = Complete;
1058 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1060 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1061 // Read* acts as play for cdda tracks in cdda mode
1065 if (cdr.SetlocPending) {
1066 seekTime = cdrSeekTime(cdr.SetSector);
1067 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1068 cdr.SetlocPending = 0;
1071 cdr.FirstSector = 1;
1073 // Fighting Force 2 - update subq time immediately
1075 ReadTrack(cdr.SetSectorPlay);
1077 CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
1079 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1084 CDR_LOG_I("Invalid command: %02x\n", Cmd);
1085 error = ERROR_INVALIDCMD;
1090 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1091 cdr.Result[1] = error;
1092 cdr.Stat = DiskError;
1096 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1097 cdr.DriveState = DRIVESTATE_STANDBY;
1098 cdr.StatP |= STATUS_ROTATING;
1101 if (!no_busy_error) {
1102 switch (cdr.DriveState) {
1103 case DRIVESTATE_LID_OPEN:
1104 case DRIVESTATE_RESCAN_CD:
1105 case DRIVESTATE_PREPARE_CD:
1107 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1108 cdr.Result[1] = ERROR_NOTREADY;
1109 cdr.Stat = DiskError;
1114 if (second_resp_time) {
1115 cdr.CmdInProgress = Cmd | 0x100;
1116 CDR_INT(second_resp_time);
1118 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1119 cdr.CmdInProgress = cdr.Cmd;
1120 CDR_LOG_I("cdrom: cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1127 #define ssat32_to_16(v) \
1128 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1130 #define ssat32_to_16(v) do { \
1131 if (v < -32768) v = -32768; \
1132 else if (v > 32767) v = 32767; \
1136 static void cdrPrepCdda(s16 *buf, int samples)
1138 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1140 for (i = 0; i < samples; i++) {
1141 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1142 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1147 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1150 int ll = cdr.AttenuatorLeftToLeft;
1151 int lr = cdr.AttenuatorLeftToRight;
1152 int rl = cdr.AttenuatorRightToLeft;
1153 int rr = cdr.AttenuatorRightToRight;
1155 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1158 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1162 for (i = 0; i < samples; i++) {
1165 l = (l * ll + r * rl) >> 7;
1166 r = (r * rr + l * lr) >> 7;
1174 for (i = 0; i < samples; i++) {
1176 l = l * (ll + rl) >> 7;
1177 //r = r * (rr + lr) >> 7;
1185 static void cdrReadInterruptSetResult(unsigned char result)
1188 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1189 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1190 cdr.CmdInProgress, cdr.Stat);
1191 cdr.Irq1Pending = result;
1195 cdr.Result[0] = result;
1196 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1200 static void cdrUpdateTransferBuf(const u8 *buf)
1204 memcpy(cdr.Transfer, buf, DATA_SIZE);
1205 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1206 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1207 if (cdr.FifoOffset < 2048 + 12)
1208 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1211 static void cdrReadInterrupt(void)
1213 u8 *buf = NULL, *hdr;
1215 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1217 ReadTrack(cdr.SetSectorPlay);
1219 buf = CDR_getBuffer();
1224 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1225 memset(cdr.Transfer, 0, DATA_SIZE);
1226 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1230 if (!cdr.Irq1Pending)
1231 cdrUpdateTransferBuf(buf);
1233 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1235 // Firemen 2: Multi-XA files - briefings, cutscenes
1236 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1238 cdr.Channel = hdr[1];
1242 * Skips playing on channel 255.
1243 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1244 * TODO : Check if this is the proper behaviour.
1246 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1247 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1249 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1250 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1251 cdr.FirstSector = 0;
1253 else cdr.FirstSector = -1;
1258 Croc 2: $40 - only FORM1 (*)
1259 Judge Dredd: $C8 - only FORM1 (*)
1260 Sim Theme Park - no adpcm at all (zero)
1263 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1264 cdrReadInterruptSetResult(cdr.StatP);
1266 cdr.SetSectorPlay[2]++;
1267 if (cdr.SetSectorPlay[2] == 75) {
1268 cdr.SetSectorPlay[2] = 0;
1269 cdr.SetSectorPlay[1]++;
1270 if (cdr.SetSectorPlay[1] == 60) {
1271 cdr.SetSectorPlay[1] = 0;
1272 cdr.SetSectorPlay[0]++;
1276 if (!cdr.Irq1Pending) {
1277 // update for CdlGetlocP
1278 ReadTrack(cdr.SetSectorPlay);
1281 CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1290 bit 5 - 1 result ready
1292 bit 7 - 1 command being processed
1295 unsigned char cdrRead0(void) {
1296 if (cdr.ResultReady)
1301 cdr.Ctrl |= 0x40; // data fifo not empty
1303 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1306 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1308 return psxHu8(0x1800) = cdr.Ctrl;
1311 void cdrWrite0(unsigned char rt) {
1312 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1314 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1317 unsigned char cdrRead1(void) {
1318 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1319 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1323 if (cdr.ResultP == cdr.ResultC)
1324 cdr.ResultReady = 0;
1326 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1328 return psxHu8(0x1801);
1331 void cdrWrite1(unsigned char rt) {
1332 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1333 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1335 switch (cdr.Ctrl & 3) {
1339 cdr.AttenuatorRightToRightT = rt;
1345 #ifdef CDR_LOG_CMD_IRQ
1346 SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1349 SysPrintf(" Param[%d] = {", cdr.ParamC);
1350 for (i = 0; i < cdr.ParamC; i++)
1351 SysPrintf(" %x,", cdr.Param[i]);
1358 cdr.ResultReady = 0;
1361 if (!cdr.CmdInProgress) {
1362 cdr.CmdInProgress = rt;
1363 // should be something like 12k + controller delays
1367 CDR_LOG_I("cdr: cmd while busy: %02x, prev %02x, busy %02x\n",
1368 rt, cdr.Cmd, cdr.CmdInProgress);
1369 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1370 cdr.CmdInProgress = rt;
1376 unsigned char cdrRead2(void) {
1377 unsigned char ret = 0;
1379 if (cdr.FifoOffset < cdr.FifoSize)
1380 ret = cdr.Transfer[cdr.FifoOffset++];
1382 CDR_LOG_I("cdrom: read empty fifo (%d)\n", cdr.FifoSize);
1384 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1388 void cdrWrite2(unsigned char rt) {
1389 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1390 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1392 switch (cdr.Ctrl & 3) {
1394 if (cdr.ParamC < 8) // FIXME: size and wrapping
1395 cdr.Param[cdr.ParamC++] = rt;
1402 cdr.AttenuatorLeftToLeftT = rt;
1405 cdr.AttenuatorRightToLeftT = rt;
1410 unsigned char cdrRead3(void) {
1412 psxHu8(0x1803) = cdr.Stat | 0xE0;
1414 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1416 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1417 return psxHu8(0x1803);
1420 void cdrWrite3(unsigned char rt) {
1421 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1422 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1424 switch (cdr.Ctrl & 3) {
1428 if (cdr.Stat & rt) {
1429 #ifdef CDR_LOG_CMD_IRQ
1430 SysPrintf("ack %02x (w %02x)\n", cdr.Stat & rt, rt);
1432 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) &&
1433 (cdr.CmdInProgress || cdr.Irq1Pending))
1434 CDR_INT(2000); // 710+
1442 cdr.AttenuatorLeftToRightT = rt;
1446 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1447 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1448 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1449 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1455 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1456 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1458 else if (rt & 0x80) {
1459 switch (cdr.Mode & 0x30) {
1460 case MODE_SIZE_2328:
1462 cdr.FifoOffset = 12;
1463 cdr.FifoSize = 2048 + 12;
1466 case MODE_SIZE_2340:
1469 cdr.FifoSize = 2340;
1473 else if (!(rt & 0xc0))
1474 cdr.FifoOffset = DATA_SIZE; // fifo empty
1477 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1482 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1484 switch (chcr & 0x71000000) {
1486 ptr = (u8 *)PSXM(madr);
1488 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1492 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1495 GS CDX: Enhancement CD crash
1498 - Spams DMA3 and gets buffer overrun
1500 size = DATA_SIZE - cdr.FifoOffset;
1505 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1506 cdr.FifoOffset += size;
1507 psxCpu->Clear(madr, size / 4);
1510 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1512 CDRDMA_INT((cdsize/4) * 24);
1514 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1516 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1517 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1521 psxRegs.cycle += (cdsize/4) * 24 - 20;
1526 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1530 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1534 void cdrDmaInterrupt(void)
1536 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1538 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1543 static void getCdInfo(void)
1547 CDR_getTN(cdr.ResultTN);
1548 CDR_getTD(0, cdr.SetSectorEnd);
1549 tmp = cdr.SetSectorEnd[0];
1550 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1551 cdr.SetSectorEnd[2] = tmp;
1555 memset(&cdr, 0, sizeof(cdr));
1561 cdr.DriveState = DRIVESTATE_STANDBY;
1562 cdr.StatP = STATUS_ROTATING;
1563 cdr.FifoOffset = DATA_SIZE; // fifo empty
1565 // BIOS player - default values
1566 cdr.AttenuatorLeftToLeft = 0x80;
1567 cdr.AttenuatorLeftToRight = 0x00;
1568 cdr.AttenuatorRightToLeft = 0x00;
1569 cdr.AttenuatorRightToRight = 0x80;
1574 int cdrFreeze(void *f, int Mode) {
1578 if (Mode == 0 && !Config.Cdda)
1581 cdr.freeze_ver = 0x63647202;
1582 gzfreeze(&cdr, sizeof(cdr));
1585 cdr.ParamP = cdr.ParamC;
1586 tmp = cdr.FifoOffset;
1589 gzfreeze(&tmp, sizeof(tmp));
1594 cdr.FifoOffset = tmp;
1595 cdr.FifoSize = (cdr.Mode & 0x20) ? 2340 : 2048 + 12;
1597 // read right sub data
1598 tmpp[0] = btoi(cdr.Prev[0]);
1599 tmpp[1] = btoi(cdr.Prev[1]);
1600 tmpp[2] = btoi(cdr.Prev[2]);
1605 if (cdr.freeze_ver < 0x63647202)
1606 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1608 Find_CurTrack(cdr.SetSectorPlay);
1610 CDR_play(cdr.SetSectorPlay);
1611 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1612 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1615 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1616 // old versions did not latch Reg2, have to fixup..
1617 if (cdr.Reg2 == 0) {
1618 SysPrintf("cdrom: fixing up old savestate\n");
1621 // also did not save Attenuator..
1622 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1623 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1625 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1633 void LidInterrupt(void) {
1635 cdrLidSeekInterrupt();