X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=libpcsxcore%2Fcdrom.c;h=2b30e89d43524af5281d785992ace5ddb9865096;hb=d9a0249331e3f0da8820e158baaafa69e3a534ac;hp=0bb2d57b3f00a34d6e08524c09ba5f2f028568ea;hpb=449968efe945cc876a0805525b90d38b96acf4c0;p=pcsx_rearmed.git diff --git a/libpcsxcore/cdrom.c b/libpcsxcore/cdrom.c index 0bb2d57b..2b30e89d 100644 --- a/libpcsxcore/cdrom.c +++ b/libpcsxcore/cdrom.c @@ -44,8 +44,76 @@ #endif //#define CDR_LOG_CMD_IRQ -cdrStruct cdr; +static struct { + unsigned char OCUP; + unsigned char Reg1Mode; + unsigned char Reg2; + unsigned char CmdProcess; + unsigned char Ctrl; + unsigned char Stat; + + unsigned char StatP; + + unsigned char Transfer[DATA_SIZE]; + struct { + unsigned char Track; + unsigned char Index; + unsigned char Relative[3]; + unsigned char Absolute[3]; + } subq; + unsigned char TrackChanged; + unsigned char pad1[3]; + unsigned int freeze_ver; + + unsigned char Prev[4]; + unsigned char Param[8]; + unsigned char Result[16]; + + unsigned char ParamC; + unsigned char ParamP; + unsigned char ResultC; + unsigned char ResultP; + unsigned char ResultReady; + unsigned char Cmd; + unsigned char Readed; + unsigned char SetlocPending; + u32 Reading; + + unsigned char ResultTN[6]; + unsigned char ResultTD[4]; + unsigned char SetSectorPlay[4]; + unsigned char SetSectorEnd[4]; + unsigned char SetSector[4]; + unsigned char Track; + boolean Play, Muted; + int CurTrack; + int Mode, File, Channel; + int Reset; + int NoErr; + int FirstSector; + + xa_decode_t Xa; + + int Init; + + u16 Irq; + u8 IrqRepeated; + u32 eCycle; + + u8 pad2; + + u8 DriveState; + u8 FastForward; + u8 FastBackward; + u8 pad; + + u8 AttenuatorLeftToLeft, AttenuatorLeftToRight; + u8 AttenuatorRightToRight, AttenuatorRightToLeft; + u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT; + u8 AttenuatorRightToRightT, AttenuatorRightToLeftT; +} cdr; static unsigned char *pTransfer; +static s16 read_buf[CD_FRAMESIZE_RAW/2]; /* CD-ROM magic numbers */ #define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */ @@ -58,12 +126,12 @@ static unsigned char *pTransfer; #define CdlStandby 7 #define CdlStop 8 #define CdlPause 9 -#define CdlInit 10 +#define CdlReset 10 #define CdlMute 11 #define CdlDemute 12 #define CdlSetfilter 13 #define CdlSetmode 14 -#define CdlGetmode 15 +#define CdlGetparam 15 #define CdlGetlocL 16 #define CdlGetlocP 17 #define CdlReadT 18 @@ -76,20 +144,22 @@ static unsigned char *pTransfer; #define CdlTest 25 #define CdlID 26 #define CdlReadS 27 -#define CdlReset 28 +#define CdlInit 28 #define CdlGetQ 29 #define CdlReadToc 30 -char *CmdName[0x100]= { +#ifdef CDR_LOG_CMD_IRQ +static const char * const CmdName[0x100] = { "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay", "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby", - "CdlStop", "CdlPause", "CdlInit", "CdlMute", - "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetmode", + "CdlStop", "CdlPause", "CdlReset", "CdlMute", + "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam", "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN", "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock", "CdlGetclock", "CdlTest", "CdlID", "CdlReadS", - "CdlReset", NULL, "CDlReadToc", NULL + "CdlInit", NULL, "CDlReadToc", NULL }; +#endif unsigned char Test04[] = { 0 }; unsigned char Test05[] = { 0 }; @@ -137,19 +207,13 @@ unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 }; #define cdReadTime (PSXCLK / 75) enum drive_state { - DRIVESTATE_STANDBY = 0, + DRIVESTATE_STANDBY = 0, // pause, play, read DRIVESTATE_LID_OPEN, DRIVESTATE_RESCAN_CD, DRIVESTATE_PREPARE_CD, DRIVESTATE_STOPPED, }; -// for cdr.Seeked -enum seeked_state { - SEEK_PENDING = 0, - SEEK_DONE = 1, -}; - static struct CdrStat stat; static unsigned int msf2sec(const u8 *msf) { @@ -177,8 +241,8 @@ static void sec2msf(unsigned int s, u8 *msf) { new_dyna_set_event(PSXINT_CDR, eCycle); \ } -// cdrReadInterrupt -#define CDREAD_INT(eCycle) { \ +// cdrPlaySeekReadInterrupt +#define CDRPLAYSEEKREAD_INT(eCycle) { \ psxRegs.interrupt |= (1 << PSXINT_CDREAD); \ psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \ psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \ @@ -193,31 +257,21 @@ static void sec2msf(unsigned int s, u8 *msf) { new_dyna_set_event(PSXINT_CDRLID, eCycle); \ } -// cdrPlayInterrupt -#define CDRMISC_INT(eCycle) { \ - psxRegs.interrupt |= (1 << PSXINT_CDRPLAY); \ - psxRegs.intCycle[PSXINT_CDRPLAY].cycle = eCycle; \ - psxRegs.intCycle[PSXINT_CDRPLAY].sCycle = psxRegs.cycle; \ - new_dyna_set_event(PSXINT_CDRPLAY, eCycle); \ -} - #define StopReading() { \ - if (cdr.Reading) { \ - cdr.Reading = 0; \ - psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \ - } \ - cdr.StatP &= ~(STATUS_READ|STATUS_SEEK);\ + cdr.Reading = 0; \ + psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \ } #define StopCdda() { \ - if (cdr.Play) { \ - if (!Config.Cdda) CDR_stop(); \ - cdr.StatP &= ~STATUS_PLAY; \ - cdr.Play = FALSE; \ - cdr.FastForward = 0; \ - cdr.FastBackward = 0; \ - /*SPU_registerCallback( SPUirq );*/ \ - } \ + if (cdr.Play && !Config.Cdda) CDR_stop(); \ + cdr.Play = FALSE; \ + cdr.FastForward = 0; \ + cdr.FastBackward = 0; \ +} + +#define SetPlaySeekRead(x, f) { \ + x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \ + x |= f; \ } #define SetResultSize(size) { \ @@ -226,27 +280,39 @@ static void sec2msf(unsigned int s, u8 *msf) { cdr.ResultReady = 1; \ } -static void setIrq(void) +static void setIrq(int log_cmd) { if (cdr.Stat & cdr.Reg2) psxHu32ref(0x1070) |= SWAP32((u32)0x4); + +#ifdef CDR_LOG_CMD_IRQ + { + int i; + SysPrintf("CDR IRQ=%d cmd %02x stat %02x: ", + !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat); + for (i = 0; i < cdr.ResultC; i++) + SysPrintf("%02x ", cdr.Result[i]); + SysPrintf("\n"); + } +#endif } // timing used in this function was taken from tests on real hardware // (yes it's slow, but you probably don't want to modify it) -void cdrLidSeekInterrupt() +void cdrLidSeekInterrupt(void) { switch (cdr.DriveState) { default: case DRIVESTATE_STANDBY: - cdr.StatP &= ~STATUS_SEEK; + StopCdda(); + StopReading(); + SetPlaySeekRead(cdr.StatP, 0); if (CDR_getStatus(&stat) == -1) return; if (stat.Status & STATUS_SHELLOPEN) { - StopCdda(); cdr.DriveState = DRIVESTATE_LID_OPEN; CDRLID_INT(0x800); } @@ -258,7 +324,6 @@ void cdrLidSeekInterrupt() // 02, 12, 10 if (!(cdr.StatP & STATUS_SHELLOPEN)) { - StopReading(); cdr.StatP |= STATUS_SHELLOPEN; // could generate error irq here, but real hardware @@ -382,7 +447,7 @@ static void ReadTrack(const u8 *time) { CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]); - cdr.RErr = CDR_readTrack(tmp); + cdr.NoErr = CDR_readTrack(tmp); memcpy(cdr.Prev, tmp, 3); if (CheckSBI(time)) @@ -431,6 +496,10 @@ static void AddIrqQueue(unsigned short irq, unsigned long ecycle) { static void cdrPlayInterrupt_Autopause() { + u32 abs_lev_max = 0; + boolean abs_lev_chselect; + u32 i; + if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) { CDR_LOG( "CDDA STOP\n" ); @@ -441,15 +510,25 @@ static void cdrPlayInterrupt_Autopause() //cdr.ResultReady = 1; //cdr.Stat = DataReady; cdr.Stat = DataEnd; - setIrq(); + setIrq(0x200); StopCdda(); + SetPlaySeekRead(cdr.StatP, 0); } - else if (cdr.Mode & MODE_REPORT) { - + else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) { cdr.Result[0] = cdr.StatP; cdr.Result[1] = cdr.subq.Track; cdr.Result[2] = cdr.subq.Index; + + abs_lev_chselect = cdr.subq.Absolute[1] & 0x01; + + /* 8 is a hack. For accuracy, it should be 588. */ + for (i = 0; i < 8; i++) + { + abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect])); + } + abs_lev_max = MIN_VALUE(abs_lev_max, 32767); + abs_lev_max |= abs_lev_chselect << 15; if (cdr.subq.Absolute[2] & 0x10) { cdr.Result[3] = cdr.subq.Relative[0]; @@ -462,45 +541,69 @@ static void cdrPlayInterrupt_Autopause() cdr.Result[5] = cdr.subq.Absolute[2]; } - cdr.Result[6] = 0; - cdr.Result[7] = 0; + cdr.Result[6] = abs_lev_max >> 0; + cdr.Result[7] = abs_lev_max >> 8; // Rayman: Logo freeze (resultready + dataready) cdr.ResultReady = 1; cdr.Stat = DataReady; SetResultSize(8); - setIrq(); + setIrq(0x201); } } -// also handles seek -void cdrPlayInterrupt() +static int cdrSeekTime(unsigned char *target) { - if (cdr.Seeked == SEEK_PENDING) { + int seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200); + /* + * Gameblabla : + * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball + * and was unreliable for that game. + * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly. + * + * Obviously, this isn't perfect but right now, it should be a bit better. + * Games to test this against if you change that setting : + * - Driver (titlescreen music delay and retry mission) + * - Worms Pinball (Will either not boot or crash in the memory card screen) + * - Viewpoint (short pauses if the delay in the ingame music is too long) + * + * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping. + * However, 1000000 is not enough for Worms Pinball to reliably boot. + */ + if(seekTime > 3386880 * 2) seekTime = 3386880 * 2; + CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime); + return seekTime; +} + +static void cdrReadInterrupt(void); + +void cdrPlaySeekReadInterrupt(void) +{ + if (cdr.Reading) { + cdrReadInterrupt(); + return; + } + + if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) { if (cdr.Stat) { CDR_LOG_I("cdrom: seek stat hack\n"); - CDRMISC_INT(0x1000); + CDRPLAYSEEKREAD_INT(0x1000); return; } SetResultSize(1); cdr.StatP |= STATUS_ROTATING; - cdr.StatP &= ~STATUS_SEEK; + SetPlaySeekRead(cdr.StatP, 0); cdr.Result[0] = cdr.StatP; - cdr.Seeked = SEEK_DONE; if (cdr.Irq == 0) { cdr.Stat = Complete; - setIrq(); + setIrq(0x202); } - if (cdr.SetlocPending) { - memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); - cdr.SetlocPending = 0; - cdr.m_locationChanged = TRUE; - } Find_CurTrack(cdr.SetSectorPlay); ReadTrack(cdr.SetSectorPlay); cdr.TrackChanged = FALSE; + return; } if (!cdr.Play) return; @@ -508,15 +611,24 @@ void cdrPlayInterrupt() CDR_LOG( "CDDA - %d:%d:%d\n", cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] ); + SetPlaySeekRead(cdr.StatP, STATUS_PLAY); if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) { StopCdda(); + SetPlaySeekRead(cdr.StatP, 0); cdr.TrackChanged = TRUE; } + else { + CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf); + } if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT))) cdrPlayInterrupt_Autopause(); - if (!cdr.Play) return; + if (CDR_readCDDA && !cdr.Muted && !Config.Cdda) { + cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1); + if (SPU_playCDDAchannel) + SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW); + } cdr.SetSectorPlay[2]++; if (cdr.SetSectorPlay[2] == 75) { @@ -528,27 +640,21 @@ void cdrPlayInterrupt() } } - if (cdr.m_locationChanged) - { - CDRMISC_INT(cdReadTime * 30); - cdr.m_locationChanged = FALSE; - } - else - { - CDRMISC_INT(cdReadTime); - } + CDRPLAYSEEKREAD_INT(cdReadTime); // update for CdlGetlocP/autopause generate_subq(cdr.SetSectorPlay); } -void cdrInterrupt() { +void cdrInterrupt(void) { u16 Irq = cdr.Irq; int no_busy_error = 0; int start_rotating = 0; int error = 0; int delay; unsigned int seekTime = 0; + u8 set_loc[3]; + int i; // Reschedule IRQ if (cdr.Stat) { @@ -582,30 +688,37 @@ void cdrInterrupt() { break; case CdlSetloc: + CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]); + + // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75 + 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)) + { + CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]); + error = ERROR_INVALIDARG; + goto set_error; + } + else + { + for (i = 0; i < 3; i++) + set_loc[i] = btoi(cdr.Param[i]); + memcpy(cdr.SetSector, set_loc, 3); + cdr.SetSector[3] = 0; + cdr.SetlocPending = 1; + } break; do_CdlPlay: case CdlPlay: StopCdda(); - if (cdr.Seeked == SEEK_PENDING) { - // XXX: wrong, should seek instead.. - cdr.Seeked = SEEK_DONE; - } - if (cdr.SetlocPending) { - memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); - cdr.SetlocPending = 0; - cdr.m_locationChanged = TRUE; - } + StopReading(); + + cdr.FastBackward = 0; + cdr.FastForward = 0; // BIOS CD Player // - Pause player, hit Track 01/02/../xx (Setloc issued!!) - if (cdr.ParamC == 0 || cdr.Param[0] == 0) { - CDR_LOG("PLAY Resume @ %d:%d:%d\n", - cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]); - } - else - { + if (cdr.ParamC != 0 && cdr.Param[0] != 0) { int track = btoi( cdr.Param[0] ); if (track <= cdr.ResultTN[1]) @@ -614,11 +727,21 @@ void cdrInterrupt() { CDR_LOG("PLAY track %d\n", cdr.CurTrack); if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) { - cdr.SetSectorPlay[0] = cdr.ResultTD[2]; - cdr.SetSectorPlay[1] = cdr.ResultTD[1]; - cdr.SetSectorPlay[2] = cdr.ResultTD[0]; + for (i = 0; i < 3; i++) + set_loc[i] = cdr.ResultTD[2 - i]; + seekTime = cdrSeekTime(set_loc); + memcpy(cdr.SetSectorPlay, set_loc, 3); } } + else if (cdr.SetlocPending) { + seekTime = cdrSeekTime(cdr.SetSector); + memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); + } + else { + CDR_LOG("PLAY Resume @ %d:%d:%d\n", + cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]); + } + cdr.SetlocPending = 0; /* Rayman: detect track changes @@ -637,16 +760,12 @@ void cdrInterrupt() { if (!Config.Cdda) CDR_play(cdr.SetSectorPlay); - // Vib Ribbon: gameplay checks flag - cdr.StatP &= ~STATUS_SEEK; - cdr.Result[0] = cdr.StatP; - - cdr.StatP |= STATUS_PLAY; + SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING); // BIOS player - set flag again cdr.Play = TRUE; - CDRMISC_INT( cdReadTime ); + CDRPLAYSEEKREAD_INT(cdReadTime + seekTime); start_rotating = 1; break; @@ -655,9 +774,7 @@ void cdrInterrupt() { cdr.Stat = Complete; // GameShark CD Player: Calls 2x + Play 2x - if( cdr.FastForward == 0 ) cdr.FastForward = 2; - else cdr.FastForward++; - + cdr.FastForward = 1; cdr.FastBackward = 0; break; @@ -665,9 +782,7 @@ void cdrInterrupt() { cdr.Stat = Complete; // GameShark CD Player: Calls 2x + Play 2x - if( cdr.FastBackward == 0 ) cdr.FastBackward = 2; - else cdr.FastBackward++; - + cdr.FastBackward = 1; cdr.FastForward = 0; break; @@ -696,6 +811,8 @@ void cdrInterrupt() { StopCdda(); StopReading(); + SetPlaySeekRead(cdr.StatP, 0); + cdr.StatP &= ~STATUS_ROTATING; delay = 0x800; if (cdr.DriveState == DRIVESTATE_STANDBY) @@ -706,12 +823,12 @@ void cdrInterrupt() { break; case CdlStop + 0x100: - cdr.StatP &= ~STATUS_ROTATING; - cdr.Result[0] = cdr.StatP; cdr.Stat = Complete; break; case CdlPause: + StopCdda(); + StopReading(); /* Gundam Battle Assault 2: much slower (*) - Fixes boot, gameplay @@ -722,23 +839,40 @@ void cdrInterrupt() { InuYasha - Feudal Fairy Tale: slower - Fixes battles */ - AddIrqQueue(CdlPause + 0x100, cdReadTime * 3); + /* Gameblabla - Tightening the timings (as taken from Duckstation). + * The timings from Duckstation are based upon hardware tests. + * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode, + * seems to be timing sensitive as it can depend on the CPU's clock speed. + * */ + if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ))) + { + delay = 7000; + } + else + { + delay = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * (1000000)); + } + AddIrqQueue(CdlPause + 0x100, delay); + SetPlaySeekRead(cdr.StatP, 0); cdr.Ctrl |= 0x80; break; case CdlPause + 0x100: - cdr.StatP &= ~STATUS_READ; - cdr.Result[0] = cdr.StatP; cdr.Stat = Complete; break; - case CdlInit: - AddIrqQueue(CdlInit + 0x100, cdReadTime * 6); + case CdlReset: + StopCdda(); + StopReading(); + SetPlaySeekRead(cdr.StatP, 0); + cdr.Muted = FALSE; + cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */ + AddIrqQueue(CdlReset + 0x100, 4100000); no_busy_error = 1; start_rotating = 1; break; - case CdlInit + 0x100: + case CdlReset + 0x100: cdr.Stat = Complete; break; @@ -759,13 +893,13 @@ void cdrInterrupt() { no_busy_error = 1; break; - case CdlGetmode: - SetResultSize(6); + case CdlGetparam: + /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */ + SetResultSize(5); cdr.Result[1] = cdr.Mode; - cdr.Result[2] = cdr.File; - cdr.Result[3] = cdr.Channel; - cdr.Result[4] = 0; - cdr.Result[5] = 0; + cdr.Result[2] = 0; + cdr.Result[3] = cdr.File; + cdr.Result[4] = cdr.Channel; no_busy_error = 1; break; @@ -777,9 +911,6 @@ void cdrInterrupt() { case CdlGetlocP: SetResultSize(8); memcpy(&cdr.Result, &cdr.subq, 8); - - if (!cdr.Play && !cdr.Reading) - cdr.Result[1] = 0; // HACK? break; case CdlReadT: // SetSession? @@ -815,7 +946,9 @@ void cdrInterrupt() { cdr.Result[0] = cdr.StatP; cdr.Result[1] = itob(cdr.ResultTD[2]); cdr.Result[2] = itob(cdr.ResultTD[1]); - cdr.Result[3] = itob(cdr.ResultTD[0]); + /* According to Nocash's documentation, the function doesn't care about ff. + * This can be seen also in Mednafen's implementation. */ + //cdr.Result[3] = itob(cdr.ResultTD[0]); } break; @@ -823,8 +956,10 @@ void cdrInterrupt() { case CdlSeekP: StopCdda(); StopReading(); - cdr.StatP |= STATUS_SEEK; + SetPlaySeekRead(cdr.StatP, STATUS_SEEK); + seekTime = cdrSeekTime(cdr.SetSector); + memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); /* Crusaders of Might and Magic = 0.5x-4x - fix cutscene speech start @@ -838,8 +973,7 @@ void cdrInterrupt() { Rockman X5 = 0.5-4x - fix capcom logo */ - CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4); - cdr.Seeked = SEEK_PENDING; + CDRPLAYSEEKREAD_INT(cdReadTime + seekTime); start_rotating = 1; break; @@ -888,7 +1022,10 @@ void cdrInterrupt() { cdr.Stat = Complete; break; - case CdlReset: + case CdlInit: + StopCdda(); + StopReading(); + SetPlaySeekRead(cdr.StatP, 0); // yes, it really sets STATUS_SHELLOPEN cdr.StatP |= STATUS_SHELLOPEN; cdr.DriveState = DRIVESTATE_RESCAN_CD; @@ -898,8 +1035,7 @@ void cdrInterrupt() { break; case CdlGetQ: - // TODO? - CDR_LOG_I("got CdlGetQ\n"); + no_busy_error = 1; break; case CdlReadToc: @@ -915,19 +1051,18 @@ void cdrInterrupt() { case CdlReadN: case CdlReadS: - if (cdr.SetlocPending) { - seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(cdr.SetSector)) * (cdReadTime / 200); - if(seekTime > 1000000) seekTime = 1000000; - memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); - cdr.SetlocPending = 0; - cdr.m_locationChanged = TRUE; - } - Find_CurTrack(cdr.SetSectorPlay); + Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay); if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1) // Read* acts as play for cdda tracks in cdda mode goto do_CdlPlay; + StopCdda(); + if (cdr.SetlocPending) { + seekTime = cdrSeekTime(cdr.SetSector); + memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); + cdr.SetlocPending = 0; + } cdr.Reading = 1; cdr.FirstSector = 1; @@ -949,24 +1084,28 @@ void cdrInterrupt() { - fixes cutscenes C-12 - Final Resistance - doesn't like seek */ + + /* + By nicolasnoble from PCSX Redux : + "It LOOKS like this logic is wrong, therefore disabling it with `&& false` for now. + For "PoPoLoCrois Monogatari II", the game logic will soft lock and will never issue GetLocP to detect + the end of its XA streams, as it seems to assume ReadS will not return a status byte with the SEEK + flag set. I think the reasonning is that since it's invalid to call GetLocP while seeking, the game + tries to protect itself against errors by preventing from issuing a GetLocP while it knows the + last status was "seek". But this makes the logic just softlock as it'll never get a notification + about the fact the drive is done seeking and the read actually started. + + In other words, this state machine here is probably wrong in assuming the response to ReadS/ReadN is + done right away. It's rather when it's done seeking, and the read has actually started. This probably + requires a bit more work to make sure seek delays are processed properly. + Checked with a few games, this seems to work fine." + + Gameblabla additional notes : + This still needs the "+ seekTime" that PCSX Redux doesn't have for the Driver "retry" mission error. + */ + CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime); - if (cdr.Seeked != SEEK_DONE) { - cdr.StatP |= STATUS_SEEK; - cdr.StatP &= ~STATUS_READ; - - // Crusaders of Might and Magic - use short time - // - fix cutscene speech (startup) - - // ??? - use more accurate seek time later - CDREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1) + seekTime); - } else { - cdr.StatP |= STATUS_READ; - cdr.StatP &= ~STATUS_SEEK; - - CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1); - } - - cdr.Result[0] = cdr.StatP; + SetPlaySeekRead(cdr.StatP, STATUS_SEEK); start_rotating = 1; break; case CdlSync: @@ -1002,19 +1141,8 @@ void cdrInterrupt() { } finish: - setIrq(); + setIrq(Irq); cdr.ParamC = 0; - -#ifdef CDR_LOG_CMD_IRQ - { - int i; - SysPrintf("CDR IRQ %d cmd %02x stat %02x: ", - !!(cdr.Stat & cdr.Reg2), Irq, cdr.Stat); - for (i = 0; i < cdr.ResultC; i++) - SysPrintf("%02x ", cdr.Result[i]); - SysPrintf("\n"); - } -#endif } #ifdef HAVE_ARMV7 @@ -1065,37 +1193,33 @@ void cdrAttenuate(s16 *buf, int samples, int stereo) } } -void cdrReadInterrupt() { +static void cdrReadInterrupt(void) +{ u8 *buf; - if (!cdr.Reading) - return; - if (cdr.Irq || cdr.Stat) { CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat); - CDREAD_INT(0x1000); + CDRPLAYSEEKREAD_INT(2048); return; } cdr.OCUP = 1; SetResultSize(1); - cdr.StatP |= STATUS_READ|STATUS_ROTATING; - cdr.StatP &= ~STATUS_SEEK; + SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING); cdr.Result[0] = cdr.StatP; - cdr.Seeked = SEEK_DONE; ReadTrack(cdr.SetSectorPlay); buf = CDR_getBuffer(); if (buf == NULL) - cdr.RErr = -1; + cdr.NoErr = 0; - if (cdr.RErr == -1) { + if (!cdr.NoErr) { CDR_LOG_I("cdrReadInterrupt() Log: err\n"); memset(cdr.Transfer, 0, DATA_SIZE); cdr.Stat = DiskError; cdr.Result[0] |= STATUS_ERROR; - CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); + setIrq(0x205); return; } @@ -1142,13 +1266,7 @@ void cdrReadInterrupt() { cdr.Readed = 0; - uint32_t delay = (cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime; - if (cdr.m_locationChanged) { - CDREAD_INT(delay * 30); - cdr.m_locationChanged = FALSE; - } else { - CDREAD_INT(delay); - } + CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime); /* Croc 2: $40 - only FORM1 (*) @@ -1158,7 +1276,7 @@ void cdrReadInterrupt() { if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) { cdr.Stat = DataReady; - setIrq(); + setIrq(0x203); } // update for CdlGetlocP @@ -1190,13 +1308,13 @@ unsigned char cdrRead0(void) { // What means the 0x10 and the 0x08 bits? I only saw it used by the bios cdr.Ctrl |= 0x18; - CDR_LOG_IO("cdr r0: %02x\n", cdr.Ctrl); + CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl); return psxHu8(0x1800) = cdr.Ctrl; } void cdrWrite0(unsigned char rt) { - CDR_LOG_IO("cdr w0: %02x\n", rt); + CDR_LOG_IO("cdr w0.idx: %02x\n", rt); cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3); } @@ -1210,16 +1328,14 @@ unsigned char cdrRead1(void) { if (cdr.ResultP == cdr.ResultC) cdr.ResultReady = 0; - CDR_LOG_IO("cdr r1: %02x\n", psxHu8(0x1801)); + CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1); return psxHu8(0x1801); } void cdrWrite1(unsigned char rt) { - u8 set_loc[3]; - int i; - - CDR_LOG_IO("cdr w1: %02x\n", rt); + const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames; + CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt); switch (cdr.Ctrl & 3) { case 0: @@ -1237,6 +1353,7 @@ void cdrWrite1(unsigned char rt) { #ifdef CDR_LOG_CMD_IRQ SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]); if (cdr.ParamC) { + int i; SysPrintf(" Param[%d] = {", cdr.ParamC); for (i = 0; i < cdr.ParamC; i++) SysPrintf(" %x,", cdr.Param[i]); @@ -1252,55 +1369,10 @@ void cdrWrite1(unsigned char rt) { AddIrqQueue(cdr.Cmd, 0x800); switch (cdr.Cmd) { - case CdlSetloc: - CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]); - - // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75 - 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)) - { - CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]); - } - else - { - for (i = 0; i < 3; i++) - { - set_loc[i] = btoi(cdr.Param[i]); - } - - i = msf2sec(cdr.SetSectorPlay); - i = abs(i - msf2sec(set_loc)); - if (i > 16) - cdr.Seeked = SEEK_PENDING; - - memcpy(cdr.SetSector, set_loc, 3); - cdr.SetSector[3] = 0; - cdr.SetlocPending = 1; - } - break; - - case CdlReadN: - case CdlReadS: - case CdlPause: - StopCdda(); - StopReading(); - break; - - case CdlReset: - case CdlInit: - cdr.Seeked = SEEK_DONE; - StopCdda(); - StopReading(); - break; - case CdlSetmode: CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]); cdr.Mode = cdr.Param[0]; - - // Squaresoft on PlayStation 1998 Collector's CD Vol. 1 - // - fixes choppy movie sound - if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 ) - StopCdda(); break; } } @@ -1314,12 +1386,13 @@ unsigned char cdrRead2(void) { ret = *pTransfer++; } - CDR_LOG_IO("cdr r2: %02x\n", ret); + CDR_LOG_IO("cdr r2.dat: %02x\n", ret); return ret; } void cdrWrite2(unsigned char rt) { - CDR_LOG_IO("cdr w2: %02x\n", rt); + const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames; + CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt); switch (cdr.Ctrl & 3) { case 0: @@ -1328,7 +1401,7 @@ void cdrWrite2(unsigned char rt) { return; case 1: cdr.Reg2 = rt; - setIrq(); + setIrq(0x204); return; case 2: cdr.AttenuatorLeftToLeftT = rt; @@ -1345,12 +1418,13 @@ unsigned char cdrRead3(void) { else psxHu8(0x1803) = cdr.Reg2 | 0xE0; - CDR_LOG_IO("cdr r3: %02x\n", psxHu8(0x1803)); + CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803)); return psxHu8(0x1803); } void cdrWrite3(unsigned char rt) { - CDR_LOG_IO("cdr w3: %02x\n", rt); + const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames; + CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt); switch (cdr.Ctrl & 3) { case 0: @@ -1468,7 +1542,7 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) { DMA_INTERRUPT(3); } -void cdrDmaInterrupt() +void cdrDmaInterrupt(void) { if (HW_DMA3_CHCR & SWAP32(0x01000000)) { @@ -1498,9 +1572,7 @@ void cdrReset() { cdr.DriveState = DRIVESTATE_STANDBY; cdr.StatP = STATUS_ROTATING; pTransfer = cdr.Transfer; - cdr.SetlocPending = 0; - cdr.m_locationChanged = FALSE; - + // BIOS player - default values cdr.AttenuatorLeftToLeft = 0x80; cdr.AttenuatorLeftToRight = 0x00; @@ -1546,6 +1618,8 @@ int cdrFreeze(void *f, int Mode) { Find_CurTrack(cdr.SetSectorPlay); if (!Config.Cdda) CDR_play(cdr.SetSectorPlay); + if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD)) + CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); } if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) { @@ -1566,8 +1640,7 @@ int cdrFreeze(void *f, int Mode) { return 0; } -void LidInterrupt() { +void LidInterrupt(void) { getCdInfo(); - StopCdda(); cdrLidSeekInterrupt(); }