// also handles seek void cdrPlayInterrupt() { if (cdr.Seeked == SEEK_PENDING) { if (cdr.Stat) { CDR_LOG_I("cdrom: seek stat hack\n"); CDRMISC_INT(0x1000); return; } SetResultSize(1); cdr.StatP |= STATUS_ROTATING; cdr.StatP &= ~STATUS_SEEK; cdr.Result[0] = cdr.StatP; cdr.Seeked = SEEK_DONE; if (cdr.Irq == 0) { cdr.Stat = Complete; setIrq(); } if (cdr.SetlocPending) { memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); cdr.SetlocPending = 0; } Find_CurTrack(cdr.SetSectorPlay); ReadTrack(cdr.SetSectorPlay); cdr.TrackChanged = FALSE; } if (!cdr.Play) return; CDR_LOG( "CDDA - %d:%d:%d\n", cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] ); if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) { StopCdda(); cdr.TrackChanged = TRUE; } if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT))) cdrPlayInterrupt_Autopause(); if (!cdr.Play) return; cdr.SetSectorPlay[2]++; if (cdr.SetSectorPlay[2] == 75) { cdr.SetSectorPlay[2] = 0; cdr.SetSectorPlay[1]++; if (cdr.SetSectorPlay[1] == 60) { cdr.SetSectorPlay[1] = 0; cdr.SetSectorPlay[0]++; } } CDRMISC_INT(cdReadTime); // update for CdlGetlocP/autopause generate_subq(cdr.SetSectorPlay); }
static void AddIrqQueue(unsigned short irq, unsigned long ecycle) { if (cdr.Irq != 0) { if (irq == cdr.Irq || irq + 0x100 == cdr.Irq) { cdr.IrqRepeated = 1; CDR_INT(ecycle); return; } CDR_LOG_I("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq); } cdr.Irq = irq; cdr.eCycle = ecycle; CDR_INT(ecycle); }
static void ReadTrack(const u8 *time) { unsigned char tmp[3]; struct SubQ *subq; u16 crc; tmp[0] = itob(time[0]); tmp[1] = itob(time[1]); tmp[2] = itob(time[2]); if (memcmp(cdr.Prev, tmp, 3) == 0) return; CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]); cdr.RErr = CDR_readTrack(tmp); memcpy(cdr.Prev, tmp, 3); //senquack - PPF patch file support; not yet added (TODO?) #if 0 if (CheckSBI(time)) return; #endif subq = (struct SubQ *)CDR_getBufferSub(); if (subq != NULL && cdr.CurTrack == 1) { crc = calcCrc((u8 *)subq + 12, 10); if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) { cdr.subq.Track = subq->TrackNumber; cdr.subq.Index = subq->IndexNumber; memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3); memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3); } else { CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]); } } else { generate_subq(time); } CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n", cdr.subq.Track, cdr.subq.Index, cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2], cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]); }
void cdrInterrupt() { u16 Irq = cdr.Irq; int no_busy_error = 0; int start_rotating = 0; int error = 0; int delay; // Reschedule IRQ if (cdr.Stat) { CDR_LOG_I("cdrom: stat hack: %02x %x\n", cdr.Irq, cdr.Stat); CDR_INT(0x1000); return; } cdr.Ctrl &= ~0x80; // default response SetResultSize(1); cdr.Result[0] = cdr.StatP; cdr.Stat = Acknowledge; if (cdr.IrqRepeated) { cdr.IrqRepeated = 0; if (cdr.eCycle > psxRegs.cycle) { CDR_INT(cdr.eCycle); goto finish; } } cdr.Irq = 0; switch (Irq) { case CdlSync: // TOOD: sometimes/always return error? break; case CdlNop: if (cdr.DriveState != DRIVESTATE_LID_OPEN) cdr.StatP &= ~STATUS_SHELLOPEN; no_busy_error = 1; break; case CdlSetloc: 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; } // 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 { int track = btoi( cdr.Param[0] ); if (track <= cdr.ResultTN[1]) cdr.CurTrack = track; 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]; } } /* Rayman: detect track changes - fixes logo freeze Twisted Metal 2: skip PREGAP + starting accurate SubQ - plays tracks without retry play Wild 9: skip PREGAP + starting accurate SubQ - plays tracks without retry play */ Find_CurTrack(cdr.SetSectorPlay); ReadTrack(cdr.SetSectorPlay); cdr.TrackChanged = FALSE; 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; // BIOS player - set flag again cdr.Play = TRUE; CDRMISC_INT( cdReadTime ); start_rotating = 1; break; case CdlForward: // TODO: error 80 if stopped cdr.Stat = Complete; // GameShark CD Player: Calls 2x + Play 2x if( cdr.FastForward == 0 ) cdr.FastForward = 2; else cdr.FastForward++; cdr.FastBackward = 0; break; case CdlBackward: cdr.Stat = Complete; // GameShark CD Player: Calls 2x + Play 2x if( cdr.FastBackward == 0 ) cdr.FastBackward = 2; else cdr.FastBackward++; cdr.FastForward = 0; break; case CdlStandby: if (cdr.DriveState != DRIVESTATE_STOPPED) { error = ERROR_INVALIDARG; goto set_error; } AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2); start_rotating = 1; break; case CdlStandby + 0x100: cdr.Stat = Complete; break; case CdlStop: if (cdr.Play) { // grab time for current track CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD); cdr.SetSectorPlay[0] = cdr.ResultTD[2]; cdr.SetSectorPlay[1] = cdr.ResultTD[1]; cdr.SetSectorPlay[2] = cdr.ResultTD[0]; } StopCdda(); StopReading(); delay = 0x800; if (cdr.DriveState == DRIVESTATE_STANDBY) delay = cdReadTime * 30 / 2; cdr.DriveState = DRIVESTATE_STOPPED; AddIrqQueue(CdlStop + 0x100, delay); break; case CdlStop + 0x100: cdr.StatP &= ~STATUS_ROTATING; cdr.Result[0] = cdr.StatP; cdr.Stat = Complete; break; case CdlPause: /* Gundam Battle Assault 2: much slower (*) - Fixes boot, gameplay Hokuto no Ken 2: slower - Fixes intro + subtitles InuYasha - Feudal Fairy Tale: slower - Fixes battles */ AddIrqQueue(CdlPause + 0x100, cdReadTime * 3); 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); no_busy_error = 1; start_rotating = 1; break; case CdlInit + 0x100: cdr.Stat = Complete; break; case CdlMute: cdr.Muted = TRUE; break; case CdlDemute: cdr.Muted = FALSE; break; case CdlSetfilter: cdr.File = cdr.Param[0]; cdr.Channel = cdr.Param[1]; break; case CdlSetmode: no_busy_error = 1; break; case CdlGetmode: SetResultSize(6); cdr.Result[1] = cdr.Mode; cdr.Result[2] = cdr.File; cdr.Result[3] = cdr.Channel; cdr.Result[4] = 0; cdr.Result[5] = 0; no_busy_error = 1; break; case CdlGetlocL: SetResultSize(8); memcpy(cdr.Result, cdr.Transfer, 8); break; case CdlGetlocP: SetResultSize(8); memcpy(&cdr.Result, &cdr.subq, 8); if (!cdr.Play && !cdr.Reading) cdr.Result[1] = 0; // HACK? break; case CdlReadT: // SetSession? // really long AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4); start_rotating = 1; break; case CdlReadT + 0x100: cdr.Stat = Complete; break; case CdlGetTN: SetResultSize(3); if (CDR_getTN(cdr.ResultTN) == -1) { cdr.Stat = DiskError; cdr.Result[0] |= STATUS_ERROR; } else { cdr.Stat = Acknowledge; cdr.Result[1] = itob(cdr.ResultTN[0]); cdr.Result[2] = itob(cdr.ResultTN[1]); } break; case CdlGetTD: cdr.Track = btoi(cdr.Param[0]); SetResultSize(4); if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) { cdr.Stat = DiskError; cdr.Result[0] |= STATUS_ERROR; } else { cdr.Stat = Acknowledge; 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]); } break; case CdlSeekL: case CdlSeekP: StopCdda(); StopReading(); cdr.StatP |= STATUS_SEEK; /* Crusaders of Might and Magic = 0.5x-4x - fix cutscene speech start Eggs of Steel = 2x-? - fix new game Medievil = ?-4x - fix cutscene speech Rockman X5 = 0.5-4x - fix capcom logo */ CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4); cdr.Seeked = SEEK_PENDING; start_rotating = 1; break; case CdlTest: switch (cdr.Param[0]) { case 0x20: // System Controller ROM Version SetResultSize(4); memcpy(cdr.Result, Test20, 4); break; case 0x22: SetResultSize(8); memcpy(cdr.Result, Test22, 4); break; case 0x23: case 0x24: SetResultSize(8); memcpy(cdr.Result, Test23, 4); break; } no_busy_error = 1; break; case CdlID: AddIrqQueue(CdlID + 0x100, 20480); break; case CdlID + 0x100: SetResultSize(8); cdr.Result[0] = cdr.StatP; cdr.Result[1] = 0; cdr.Result[2] = 0; cdr.Result[3] = 0; // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) { cdr.Result[1] = 0xc0; } else { if (stat.Type == 2) cdr.Result[1] |= 0x10; if (CdromId[0] == '\0') cdr.Result[1] |= 0x80; } cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08; strncpy((char *)&cdr.Result[4], "PCSX", 4); cdr.Stat = Complete; break; case CdlReset: // yes, it really sets STATUS_SHELLOPEN cdr.StatP |= STATUS_SHELLOPEN; cdr.DriveState = DRIVESTATE_RESCAN_CD; CDRLID_INT(20480); no_busy_error = 1; start_rotating = 1; break; case CdlGetQ: // TODO? CDR_LOG_I("got CdlGetQ\n"); break; case CdlReadToc: AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4); no_busy_error = 1; start_rotating = 1; break; case CdlReadToc + 0x100: cdr.Stat = Complete; no_busy_error = 1; break; case CdlReadN: case CdlReadS: if (cdr.SetlocPending) { memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); cdr.SetlocPending = 0; } Find_CurTrack(cdr.SetSectorPlay); if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1) // Read* acts as play for cdda tracks in cdda mode goto do_CdlPlay; cdr.Reading = 1; cdr.FirstSector = 1; // Fighting Force 2 - update subq time immediately // - fixes new game ReadTrack(cdr.SetSectorPlay); // Crusaders of Might and Magic - update getlocl now // - fixes cutscene speech { u8 *buf = CDR_getBuffer(); if (buf != NULL) memcpy(cdr.Transfer, buf, 8); } /* Duke Nukem: Land of the Babes - seek then delay read for one frame - fixes cutscenes C-12 - Final Resistance - doesn't like seek */ 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); } else { cdr.StatP |= STATUS_READ; cdr.StatP &= ~STATUS_SEEK; CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1); } cdr.Result[0] = cdr.StatP; start_rotating = 1; break; default: CDR_LOG_I("Invalid command: %02x\n", Irq); error = ERROR_INVALIDCMD; // FALLTHROUGH set_error: SetResultSize(2); cdr.Result[0] = cdr.StatP | STATUS_ERROR; cdr.Result[1] = error; cdr.Stat = DiskError; break; } if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) { cdr.DriveState = DRIVESTATE_STANDBY; cdr.StatP |= STATUS_ROTATING; } if (!no_busy_error) { switch (cdr.DriveState) { case DRIVESTATE_LID_OPEN: case DRIVESTATE_RESCAN_CD: case DRIVESTATE_PREPARE_CD: SetResultSize(2); cdr.Result[0] = cdr.StatP | STATUS_ERROR; cdr.Result[1] = ERROR_NOT_READY; cdr.Stat = DiskError; break; } } finish: setIrq(); 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 }
// also handles seek void cdrPlayInterrupt() { if (cdr.Seeked == SEEK_PENDING) { if (cdr.Stat) { CDR_LOG_I("cdrom: seek stat hack\n"); CDRMISC_INT(0x1000); return; } SetResultSize(1); cdr.StatP |= STATUS_ROTATING; cdr.StatP &= ~STATUS_SEEK; cdr.Result[0] = cdr.StatP; cdr.Seeked = SEEK_DONE; if (cdr.Irq == 0) { cdr.Stat = Complete; setIrq(); } if (cdr.SetlocPending) { memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); cdr.SetlocPending = 0; } Find_CurTrack(cdr.SetSectorPlay); ReadTrack(cdr.SetSectorPlay); cdr.TrackChanged = FALSE; } if (!cdr.Play) return; CDR_LOG( "CDDA - %d:%d:%d\n", cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] ); if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) { StopCdda(); cdr.TrackChanged = TRUE; } if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT))) cdrPlayInterrupt_Autopause(); if (!cdr.Play) return; cdr.SetSectorPlay[2]++; if (cdr.SetSectorPlay[2] == 75) { cdr.SetSectorPlay[2] = 0; cdr.SetSectorPlay[1]++; if (cdr.SetSectorPlay[1] == 60) { cdr.SetSectorPlay[1] = 0; cdr.SetSectorPlay[0]++; } } CDRMISC_INT(cdReadTime); // update for CdlGetlocP/autopause generate_subq(cdr.SetSectorPlay); //senquack - Copy/pasted CHUI's ResetIoCycle() call into this new function // TODO: Are these really beneficial/necessary? // CHUI: Añado ResetIoCycle para permite que en el proximo salto entre en psxBranchTest ResetIoCycle(); }