X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=pcsx_rearmed.git;a=blobdiff_plain;f=libpcsxcore%2Fcdrom.c;h=38fecf71996ae1f6f58d3de2ed08276f35bc9dc8;hp=0e033db76f8b54778b4d33fa107bbcb426f8973f;hb=bdd050c3ed792381df2e744fee5b7ee80b93fd68;hpb=c62b43c9f32d79116225247acf09ccd30d4d658b diff --git a/libpcsxcore/cdrom.c b/libpcsxcore/cdrom.c index 0e033db7..38fecf71 100644 --- a/libpcsxcore/cdrom.c +++ b/libpcsxcore/cdrom.c @@ -14,17 +14,37 @@ * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA. * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ -/* +/* * Handles all CD-ROM registers and functions. */ #include "cdrom.h" #include "ppf.h" +#include "psxdma.h" + +/* logging */ +#if 0 +#define CDR_LOG SysPrintf +#else +#define CDR_LOG(...) +#endif +#if 0 +#define CDR_LOG_I SysPrintf +#else +#define CDR_LOG_I(...) +#endif +#if 0 +#define CDR_LOG_IO SysPrintf +#else +#define CDR_LOG_IO(...) +#endif +//#define CDR_LOG_CMD_IRQ cdrStruct cdr; +static unsigned char *pTransfer; /* CD-ROM magic numbers */ #define CdlSync 0 @@ -56,16 +76,9 @@ cdrStruct cdr; #define CdlID 26 #define CdlReadS 27 #define CdlReset 28 +#define CdlGetQ 29 #define CdlReadToc 30 -#define AUTOPAUSE 249 -#define READ_ACK 250 -#define READ 251 -#define REPPLAY_ACK 252 -#define REPPLAY 253 -#define ASYNC 254 -/* don't set 255, it's reserved */ - char *CmdName[0x100]= { "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay", "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby", @@ -83,14 +96,79 @@ unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 }; unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F }; unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 }; +// cdr.Stat: +#define NoIntr 0 +#define DataReady 1 +#define Complete 2 +#define Acknowledge 3 +#define DataEnd 4 +#define DiskError 5 + +/* Modes flags */ +#define MODE_SPEED (1<<7) // 0x80 +#define MODE_STRSND (1<<6) // 0x40 ADPCM on/off +#define MODE_SIZE_2340 (1<<5) // 0x20 +#define MODE_SIZE_2328 (1<<4) // 0x10 +#define MODE_SIZE_2048 (0<<4) // 0x00 +#define MODE_SF (1<<3) // 0x08 channel on/off +#define MODE_REPORT (1<<2) // 0x04 +#define MODE_AUTOPAUSE (1<<1) // 0x02 +#define MODE_CDDA (1<<0) // 0x01 + +/* Status flags */ +#define STATUS_PLAY (1<<7) // 0x80 +#define STATUS_SEEK (1<<6) // 0x40 +#define STATUS_READ (1<<5) // 0x20 +#define STATUS_SHELLOPEN (1<<4) // 0x10 +#define STATUS_UNKNOWN3 (1<<3) // 0x08 +#define STATUS_UNKNOWN2 (1<<2) // 0x04 +#define STATUS_ROTATING (1<<1) // 0x02 +#define STATUS_ERROR (1<<0) // 0x01 + +/* Errors */ +#define ERROR_NOT_READY (1<<7) // 0x80 +#define ERROR_INVALIDCMD (1<<6) // 0x40 +#define ERROR_INVALIDARG (1<<5) // 0x20 + // 1x = 75 sectors per second // PSXCLK = 1 sec in the ps // so (PSXCLK / 75) = cdr read time (linuzappz) #define cdReadTime (PSXCLK / 75) +enum drive_state { + DRIVESTATE_STANDBY = 0, + 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 struct SubQ *subq; +static unsigned int msf2sec(const u8 *msf) { + return ((msf[0] * 60 + msf[1]) * 75) + msf[2]; +} + +// for that weird psemu API.. +static unsigned int fsm2sec(const u8 *msf) { + return ((msf[2] * 60 + msf[1]) * 75) + msf[0]; +} + +static void sec2msf(unsigned int s, u8 *msf) { + msf[0] = s / 75 / 60; + s = s - msf[0] * 75 * 60; + msf[1] = s / 75; + s = s - msf[1] * 75; + msf[2] = s; +} + +// cdrInterrupt #define CDR_INT(eCycle) { \ psxRegs.interrupt |= (1 << PSXINT_CDR); \ psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \ @@ -98,6 +176,7 @@ static struct SubQ *subq; new_dyna_set_event(PSXINT_CDR, eCycle); \ } +// cdrReadInterrupt #define CDREAD_INT(eCycle) { \ psxRegs.interrupt |= (1 << PSXINT_CDREAD); \ psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \ @@ -105,11 +184,20 @@ static struct SubQ *subq; new_dyna_set_event(PSXINT_CDREAD, eCycle); \ } -#define StartReading(type, eCycle) { \ - cdr.Reading = type; \ - cdr.FirstSector = 1; \ - cdr.Readed = 0xff; \ - AddIrqQueue(READ_ACK, eCycle); \ +// cdrLidSeekInterrupt +#define CDRLID_INT(eCycle) { \ + psxRegs.interrupt |= (1 << PSXINT_CDRLID); \ + psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \ + psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \ + 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() { \ @@ -117,300 +205,603 @@ static struct SubQ *subq; cdr.Reading = 0; \ psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \ } \ - cdr.StatP &= ~0x20;\ + cdr.StatP &= ~(STATUS_READ|STATUS_SEEK);\ } #define StopCdda() { \ if (cdr.Play) { \ if (!Config.Cdda) CDR_stop(); \ - cdr.StatP &= ~0x80; \ + cdr.StatP &= ~STATUS_PLAY; \ cdr.Play = FALSE; \ + cdr.FastForward = 0; \ + cdr.FastBackward = 0; \ + /*SPU_registerCallback( SPUirq );*/ \ } \ } #define SetResultSize(size) { \ - cdr.ResultP = 0; \ + cdr.ResultP = 0; \ cdr.ResultC = size; \ cdr.ResultReady = 1; \ } -static void ReadTrack() { - cdr.Prev[0] = itob(cdr.SetSector[0]); - cdr.Prev[1] = itob(cdr.SetSector[1]); - cdr.Prev[2] = itob(cdr.SetSector[2]); +static void setIrq(void) +{ + if (cdr.Stat & cdr.Reg2) + psxHu32ref(0x1070) |= SWAP32((u32)0x4); +} -#ifdef CDR_LOG - CDR_LOG("ReadTrack() Log: KEY *** %x:%x:%x\n", cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]); -#endif - cdr.RErr = CDR_readTrack(cdr.Prev); +// 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() +{ + switch (cdr.DriveState) { + default: + case DRIVESTATE_STANDBY: + cdr.StatP &= ~STATUS_SEEK; + + if (CDR_getStatus(&stat) == -1) + return; + + if (stat.Status & STATUS_SHELLOPEN) + { + StopCdda(); + cdr.DriveState = DRIVESTATE_LID_OPEN; + CDRLID_INT(0x800); + } + break; + + case DRIVESTATE_LID_OPEN: + if (CDR_getStatus(&stat) == -1) + stat.Status &= ~STATUS_SHELLOPEN; + + // 02, 12, 10 + if (!(cdr.StatP & STATUS_SHELLOPEN)) { + StopReading(); + cdr.StatP |= STATUS_SHELLOPEN; + + // could generate error irq here, but real hardware + // only sometimes does that + // (not done when lots of commands are sent?) + + CDRLID_INT(cdReadTime * 30); + break; + } + else if (cdr.StatP & STATUS_ROTATING) { + cdr.StatP &= ~STATUS_ROTATING; + } + else if (!(stat.Status & STATUS_SHELLOPEN)) { + // closed now + CheckCdrom(); + + // cdr.StatP STATUS_SHELLOPEN is "sticky" + // and is only cleared by CdlNop + + cdr.DriveState = DRIVESTATE_RESCAN_CD; + CDRLID_INT(cdReadTime * 105); + break; + } + + // recheck for close + CDRLID_INT(cdReadTime * 3); + break; + + case DRIVESTATE_RESCAN_CD: + cdr.StatP |= STATUS_ROTATING; + cdr.DriveState = DRIVESTATE_PREPARE_CD; + + // this is very long on real hardware, over 6 seconds + // make it a bit faster here... + CDRLID_INT(cdReadTime * 150); + break; + + case DRIVESTATE_PREPARE_CD: + cdr.StatP |= STATUS_SEEK; + + cdr.DriveState = DRIVESTATE_STANDBY; + CDRLID_INT(cdReadTime * 26); + break; + } } -// cdr.Stat: -#define NoIntr 0 -#define DataReady 1 -#define Complete 2 -#define Acknowledge 3 -#define DataEnd 4 -#define DiskError 5 +static void Find_CurTrack(const u8 *time) +{ + int current, sect; + + current = msf2sec(time); + + for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) { + CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD); + sect = fsm2sec(cdr.ResultTD); + if (sect - current >= 150) + break; + } +} + +static void generate_subq(const u8 *time) +{ + unsigned char start[3], next[3]; + unsigned int this_s, start_s, next_s, pregap; + int relative_s; + + CDR_getTD(cdr.CurTrack, start); + if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) { + pregap = 150; + CDR_getTD(cdr.CurTrack + 1, next); + } + else { + // last track - cd size + pregap = 0; + next[0] = cdr.SetSectorEnd[2]; + next[1] = cdr.SetSectorEnd[1]; + next[2] = cdr.SetSectorEnd[0]; + } + + this_s = msf2sec(time); + start_s = fsm2sec(start); + next_s = fsm2sec(next); + + cdr.TrackChanged = FALSE; + + if (next_s - this_s < pregap) { + cdr.TrackChanged = TRUE; + cdr.CurTrack++; + start_s = next_s; + } + + cdr.subq.Index = 1; + + relative_s = this_s - start_s; + if (relative_s < 0) { + cdr.subq.Index = 0; + relative_s = -relative_s; + } + sec2msf(relative_s, cdr.subq.Relative); + + cdr.subq.Track = itob(cdr.CurTrack); + cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]); + cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]); + cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]); + cdr.subq.Absolute[0] = itob(time[0]); + cdr.subq.Absolute[1] = itob(time[1]); + cdr.subq.Absolute[2] = itob(time[2]); +} + +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); + + if (CheckSBI(time)) + return; + + 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]); +} + +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); + } -void AddIrqQueue(unsigned char irq, unsigned long ecycle) { cdr.Irq = irq; - if (cdr.Stat) { - cdr.eCycle = ecycle; - } else { - CDR_INT(ecycle); + cdr.eCycle = ecycle; + + CDR_INT(ecycle); +} + +static void cdrPlayInterrupt_Autopause() +{ + if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) { + CDR_LOG( "CDDA STOP\n" ); + + // Magic the Gathering + // - looping territory cdda + + // ...? + //cdr.ResultReady = 1; + //cdr.Stat = DataReady; + cdr.Stat = DataEnd; + setIrq(); + + StopCdda(); + } + else if (cdr.Mode & MODE_REPORT) { + + cdr.Result[0] = cdr.StatP; + cdr.Result[1] = cdr.subq.Track; + cdr.Result[2] = cdr.subq.Index; + + if (cdr.subq.Absolute[2] & 0x10) { + cdr.Result[3] = cdr.subq.Relative[0]; + cdr.Result[4] = cdr.subq.Relative[1] | 0x80; + cdr.Result[5] = cdr.subq.Relative[2]; + } + else { + cdr.Result[3] = cdr.subq.Absolute[0]; + cdr.Result[4] = cdr.subq.Absolute[1]; + cdr.Result[5] = cdr.subq.Absolute[2]; + } + + cdr.Result[6] = 0; + cdr.Result[7] = 0; + + // Rayman: Logo freeze (resultready + dataready) + cdr.ResultReady = 1; + cdr.Stat = DataReady; + + SetResultSize(8); + setIrq(); + } +} + +// 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); } void cdrInterrupt() { - int i; - unsigned char Irq = cdr.Irq; + 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.Irq = 0xff; 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: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + // TOOD: sometimes/always return error? break; case CdlNop: - SetResultSize(1); - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - i = stat.Status; - if (CDR_getStatus(&stat) != -1) { - if (stat.Type == 0xff) cdr.Stat = DiskError; - if (stat.Status & 0x10) { - cdr.Stat = DiskError; - cdr.Result[0] |= 0x11; - cdr.Result[0] &= ~0x02; - } - else if (i & 0x10) { - cdr.StatP |= 0x2; - cdr.Result[0] |= 0x2; - CheckCdrom(); - } - } + if (cdr.DriveState != DRIVESTATE_LID_OPEN) + cdr.StatP &= ~STATUS_SHELLOPEN; + no_busy_error = 1; break; case CdlSetloc: - cdr.CmdProcess = 0; - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; break; + do_CdlPlay: case CdlPlay: - cdr.CmdProcess = 0; - SetResultSize(1); - cdr.StatP |= 0x2; + 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.Stat = Acknowledge; - cdr.StatP |= 0x80; -// if ((cdr.Mode & 0x5) == 0x5) AddIrqQueue(REPPLAY, cdReadTime); + + cdr.StatP |= STATUS_PLAY; + + // BIOS player - set flag again + cdr.Play = TRUE; + + CDRMISC_INT( cdReadTime ); + start_rotating = 1; break; - case CdlForward: - cdr.CmdProcess = 0; - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + 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.CmdProcess = 0; - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + 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: - cdr.CmdProcess = 0; - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; + 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: - cdr.CmdProcess = 0; - SetResultSize(1); - cdr.StatP &= ~0x2; + 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; -// cdr.Stat = Acknowledge; - - // check case open/close -shalma - i = stat.Status; - if (CDR_getStatus(&stat) != -1) { - if (stat.Type == 0xff) cdr.Stat = DiskError; - if (stat.Status & 0x10) { - cdr.Stat = DiskError; - cdr.Result[0] |= 0x11; - cdr.Result[0] &= ~0x02; - } - else if (i & 0x10) { - cdr.StatP |= 0x2; - cdr.Result[0] |= 0x2; - CheckCdrom(); - } - } break; case CdlPause: - SetResultSize(1); - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlPause + 0x20, 0x1000); + /* + 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 + 0x20: - SetResultSize(1); - cdr.StatP &= ~0x20; - cdr.StatP |= 0x2; + case CdlPause + 0x100: + cdr.StatP &= ~STATUS_READ; cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.Stat = Complete; break; - case CdlInit: - SetResultSize(1); - cdr.StatP = 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; -// if (!cdr.Init) { - AddIrqQueue(CdlInit + 0x20, 0x1000); -// } - break; + case CdlInit: + AddIrqQueue(CdlInit + 0x100, cdReadTime * 6); + no_busy_error = 1; + start_rotating = 1; + break; - case CdlInit + 0x20: - SetResultSize(1); - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; - cdr.Init = 1; + case CdlInit + 0x100: + cdr.Stat = Complete; break; - case CdlMute: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + case CdlMute: + cdr.Muted = TRUE; break; - case CdlDemute: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + case CdlDemute: + cdr.Muted = FALSE; break; - case CdlSetfilter: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - break; + case CdlSetfilter: + cdr.File = cdr.Param[0]; + cdr.Channel = cdr.Param[1]; + break; case CdlSetmode: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - break; + no_busy_error = 1; + break; - case CdlGetmode: + case CdlGetmode: SetResultSize(6); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Result[1] = cdr.Mode; - cdr.Result[2] = cdr.File; - cdr.Result[3] = cdr.Channel; - cdr.Result[4] = 0; - cdr.Result[5] = 0; - cdr.Stat = Acknowledge; - break; + 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: + case CdlGetlocL: SetResultSize(8); -// for (i = 0; i < 8; i++) -// cdr.Result[i] = itob(cdr.Transfer[i]); - for (i = 0; i < 8; i++) - cdr.Result[i] = cdr.Transfer[i]; - cdr.Stat = Acknowledge; - break; + memcpy(cdr.Result, cdr.Transfer, 8); + break; case CdlGetlocP: SetResultSize(8); - subq = (struct SubQ *)CDR_getBufferSub(); - - if (subq != NULL) { - cdr.Result[0] = subq->TrackNumber; - cdr.Result[1] = subq->IndexNumber; - memcpy(cdr.Result + 2, subq->TrackRelativeAddress, 3); - memcpy(cdr.Result + 5, subq->AbsoluteAddress, 3); - - // subQ integrity check - if (calcCrc((u8 *)subq + 12, 10) != (((u16)subq->CRC[0] << 8) | subq->CRC[1])) { - memset(cdr.Result + 2, 0, 3 + 3); // CRC wrong, wipe out time data - } - } else { - cdr.Result[0] = 1; - cdr.Result[1] = 1; - - cdr.Result[2] = btoi(cdr.Prev[0]); - cdr.Result[3] = btoi(cdr.Prev[1]) - 2; - cdr.Result[4] = cdr.Prev[2]; - - // m:s adjustment - if ((s8)cdr.Result[3] < 0) { - cdr.Result[3] += 60; - cdr.Result[2] -= 1; - } + memcpy(&cdr.Result, &cdr.subq, 8); - cdr.Result[2] = itob(cdr.Result[2]); - cdr.Result[3] = itob(cdr.Result[3]); + if (!cdr.Play && !cdr.Reading) + cdr.Result[1] = 0; // HACK? + break; - memcpy(cdr.Result + 5, cdr.Prev, 3); - } + case CdlReadT: // SetSession? + // really long + AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4); + start_rotating = 1; + break; - cdr.Stat = Acknowledge; + case CdlReadT + 0x100: + cdr.Stat = Complete; break; - case CdlGetTN: - cdr.CmdProcess = 0; + case CdlGetTN: SetResultSize(3); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - if (CDR_getTN(cdr.ResultTN) == -1) { + if (CDR_getTN(cdr.ResultTN) == -1) { cdr.Stat = DiskError; - cdr.Result[0] |= 0x01; - } else { - cdr.Stat = Acknowledge; - cdr.Result[1] = itob(cdr.ResultTN[0]); - cdr.Result[2] = itob(cdr.ResultTN[1]); - } - break; + 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.CmdProcess = 0; + case CdlGetTD: cdr.Track = btoi(cdr.Param[0]); SetResultSize(4); - cdr.StatP |= 0x2; if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) { cdr.Stat = DiskError; - cdr.Result[0] |= 0x01; + cdr.Result[0] |= STATUS_ERROR; } else { cdr.Stat = Acknowledge; cdr.Result[0] = cdr.StatP; @@ -420,45 +811,33 @@ void cdrInterrupt() { } break; - case CdlSeekL: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.StatP |= 0x40; - cdr.Stat = Acknowledge; - cdr.Seeked = TRUE; - AddIrqQueue(CdlSeekL + 0x20, 0x1000); - break; + case CdlSeekL: + case CdlSeekP: + StopCdda(); + StopReading(); + cdr.StatP |= STATUS_SEEK; - case CdlSeekL + 0x20: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.StatP &= ~0x40; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; - break; + /* + Crusaders of Might and Magic = 0.5x-4x + - fix cutscene speech start - case CdlSeekP: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.StatP |= 0x40; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlSeekP + 0x20, 0x1000); - break; + Eggs of Steel = 2x-? + - fix new game - case CdlSeekP + 0x20: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.StatP &= ~0x40; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + 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: - cdr.Stat = Acknowledge; - switch (cdr.Param[0]) { - case 0x20: // System Controller ROM Version + switch (cdr.Param[0]) { + case 0x20: // System Controller ROM Version SetResultSize(4); memcpy(cdr.Result, Test20, 4); break; @@ -470,131 +849,209 @@ void cdrInterrupt() { SetResultSize(8); memcpy(cdr.Result, Test23, 4); break; - } + } + no_busy_error = 1; break; - case CdlID: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlID + 0x20, 0x1000); + case CdlID: + AddIrqQueue(CdlID + 0x100, 20480); break; - case CdlID + 0x20: + case CdlID + 0x100: SetResultSize(8); - if (CDR_getStatus(&stat) == -1) { - cdr.Result[0] = 0x00; // 0x08 and cdr.Result[1]|0x10 : audio cd, enters cd player - cdr.Result[1] = 0x00; // 0x80 leads to the menu in the bios, else loads CD - } - else { - if (stat.Type == 2) { - cdr.Result[0] = 0x08; - cdr.Result[1] = 0x10; - } - else { - cdr.Result[0] = 0x00; - cdr.Result[1] = 0x00; - } - } - cdr.Result[1] |= 0x80; - cdr.Result[2] = 0x00; - cdr.Result[3] = 0x00; + 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: - SetResultSize(1); - cdr.StatP = 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + // 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 CdlReadToc: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlReadToc + 0x20, 0x1000); + case CdlGetQ: + // TODO? + CDR_LOG_I("got CdlGetQ\n"); break; - case CdlReadToc + 0x20: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + case CdlReadToc: + AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4); + no_busy_error = 1; + start_rotating = 1; break; - case AUTOPAUSE: - cdr.OCUP = 0; -/* SetResultSize(1); - StopCdda(); - StopReading(); - cdr.OCUP = 0; - cdr.StatP&=~0x20; - cdr.StatP|= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = DataEnd; -*/ AddIrqQueue(CdlPause, 0x800); + case CdlReadToc + 0x100: + cdr.Stat = Complete; + no_busy_error = 1; break; - case READ_ACK: - if (!cdr.Reading) return; + 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); - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - if (!cdr.Seeked) { - cdr.Seeked = TRUE; - cdr.StatP |= 0x40; + + // Crusaders of Might and Magic - update getlocl now + // - fixes cutscene speech + { + u8 *buf = CDR_getBuffer(); + if (buf != NULL) + memcpy(cdr.Transfer, buf, 8); } - cdr.StatP |= 0x20; - cdr.Stat = Acknowledge; -// CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); - CDREAD_INT(0x80000); - break; + /* + 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); + } - case REPPLAY_ACK: - cdr.Stat = Acknowledge; cdr.Result[0] = cdr.StatP; - SetResultSize(1); - AddIrqQueue(REPPLAY, cdReadTime); + start_rotating = 1; break; - case REPPLAY: - if ((cdr.Mode & 5) != 5) break; -/* if (CDR_getStatus(&stat) == -1) { - cdr.Result[0] = 0; - cdr.Result[1] = 0; - cdr.Result[2] = 0; - cdr.Result[3] = 0; - cdr.Result[4] = 0; - cdr.Result[5] = 0; - cdr.Result[6] = 0; - cdr.Result[7] = 0; - } else memcpy(cdr.Result, &stat.Track, 8); - cdr.Stat = 1; - SetResultSize(8); - AddIrqQueue(REPPLAY_ACK, cdReadTime); -*/ 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; + } - case 0xff: - return; + if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) { + cdr.DriveState = DRIVESTATE_STANDBY; + cdr.StatP |= STATUS_ROTATING; + } - default: - cdr.Stat = Complete; + 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; + } } - if (cdr.Stat != NoIntr && cdr.Reg2 != 0x18) { - psxHu32ref(0x1070) |= SWAP32((u32)0x4); +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 +} -#ifdef CDR_LOG - CDR_LOG("cdrInterrupt() Log: CDR Interrupt IRQ %x\n", Irq); +#ifdef __ARM_ARCH_7A__ + #define ssat32_to_16(v) \ + asm("ssat %0,#16,%1" : "=r" (v) : "r" (v)) +#else + #define ssat32_to_16(v) do { \ + if (v < -32768) v = -32768; \ + else if (v > 32767) v = 32767; \ + } while (0) #endif + +void cdrAttenuate(s16 *buf, int samples, int stereo) +{ + int i, l, r; + int ll = cdr.AttenuatorLeftToLeft; + int lr = cdr.AttenuatorLeftToRight; + int rl = cdr.AttenuatorRightToLeft; + int rr = cdr.AttenuatorRightToRight; + + if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88) + return; + + if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40) + return; + + if (stereo) { + for (i = 0; i < samples; i++) { + l = buf[i * 2]; + r = buf[i * 2 + 1]; + l = (l * ll + r * rl) >> 7; + r = (r * rr + l * lr) >> 7; + ssat32_to_16(l); + ssat32_to_16(r); + buf[i * 2] = l; + buf[i * 2 + 1] = r; + } + } + else { + for (i = 0; i < samples; i++) { + l = buf[i]; + l = l * (ll + rl) >> 7; + //r = r * (rr + lr) >> 7; + ssat32_to_16(l); + //ssat32_to_16(r); + buf[i] = l; + } + } } void cdrReadInterrupt() { @@ -603,34 +1060,30 @@ void cdrReadInterrupt() { if (!cdr.Reading) return; - if (cdr.Stat) { + if (cdr.Irq || cdr.Stat) { + CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat); CDREAD_INT(0x1000); return; } -#ifdef CDR_LOG - CDR_LOG("cdrReadInterrupt() Log: KEY END"); -#endif - - cdr.OCUP = 1; + cdr.OCUP = 1; SetResultSize(1); - cdr.StatP |= 0x22; - cdr.StatP &= ~0x40; - cdr.Result[0] = cdr.StatP; + cdr.StatP |= STATUS_READ|STATUS_ROTATING; + cdr.StatP &= ~STATUS_SEEK; + cdr.Result[0] = cdr.StatP; + cdr.Seeked = SEEK_DONE; - ReadTrack(); + ReadTrack(cdr.SetSectorPlay); buf = CDR_getBuffer(); if (buf == NULL) cdr.RErr = -1; if (cdr.RErr == -1) { -#ifdef CDR_LOG - fprintf(emuLog, "cdrReadInterrupt() Log: err\n"); -#endif + CDR_LOG_I("cdrReadInterrupt() Log: err\n"); memset(cdr.Transfer, 0, DATA_SIZE); cdr.Stat = DiskError; - cdr.Result[0] |= 0x01; + cdr.Result[0] |= STATUS_ERROR; CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); return; } @@ -638,19 +1091,22 @@ void cdrReadInterrupt() { memcpy(cdr.Transfer, buf, DATA_SIZE); CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]); - cdr.Stat = DataReady; -#ifdef CDR_LOG - fprintf(emuLog, "cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]); -#endif + CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]); - if ((!cdr.Muted) && (cdr.Mode & 0x40) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA - if ((cdr.Transfer[4 + 2] & 0x4) && - ((cdr.Mode & 0x8) ? (cdr.Transfer[4 + 1] == cdr.Channel) : 1) && - (cdr.Transfer[4 + 0] == cdr.File)) { - int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector); + if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA + // Firemen 2: Multi-XA files - briefings, cutscenes + if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) { + cdr.File = cdr.Transfer[4 + 0]; + cdr.Channel = cdr.Transfer[4 + 1]; + } + if((cdr.Transfer[4 + 2] & 0x4) && + (cdr.Transfer[4 + 1] == cdr.Channel) && + (cdr.Transfer[4 + 0] == cdr.File)) { + int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector); if (!ret) { + cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo); SPU_playADPCMchannel(&cdr.Xa); cdr.FirstSector = 0; } @@ -658,35 +1114,38 @@ void cdrReadInterrupt() { } } - cdr.SetSector[2]++; - if (cdr.SetSector[2] == 75) { - cdr.SetSector[2] = 0; - cdr.SetSector[1]++; - if (cdr.SetSector[1] == 60) { - cdr.SetSector[1] = 0; - cdr.SetSector[0]++; - } - } - - cdr.Readed = 0; - - if ((cdr.Transfer[4 + 2] & 0x80) && (cdr.Mode & 0x2)) { // EOF -#ifdef CDR_LOG - CDR_LOG("cdrReadInterrupt() Log: Autopausing read\n"); -#endif -// AddIrqQueue(AUTOPAUSE, 0x1000); - AddIrqQueue(CdlPause, 0x1000); + 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]++; + } } - else { - CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); + + cdr.Readed = 0; + + CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime); + + /* + Croc 2: $40 - only FORM1 (*) + Judge Dredd: $C8 - only FORM1 (*) + Sim Theme Park - no adpcm at all (zero) + */ + + if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) { + cdr.Stat = DataReady; + setIrq(); } - psxHu32ref(0x1070) |= SWAP32((u32)0x4); + + // update for CdlGetlocP + ReadTrack(cdr.SetSectorPlay); } /* cdrRead0: - bit 0 - 0 REG1 command send / 1 REG1 data read - bit 1 - 0 data transfer finish / 1 data transfer ready/in progress + bit 0,1 - mode bit 2 - unknown bit 3 - unknown bit 4 - unknown @@ -709,57 +1168,52 @@ unsigned char cdrRead0(void) { // What means the 0x10 and the 0x08 bits? I only saw it used by the bios cdr.Ctrl |= 0x18; -#ifdef CDR_LOG - CDR_LOG("cdrRead0() Log: CD0 Read: %x\n", cdr.Ctrl); -#endif + CDR_LOG_IO("cdr r0: %02x\n", cdr.Ctrl); return psxHu8(0x1800) = cdr.Ctrl; } -/* -cdrWrite0: - 0 - to send a command / 1 - to get the result -*/ - void cdrWrite0(unsigned char rt) { -#ifdef CDR_LOG - CDR_LOG("cdrWrite0() Log: CD0 write: %x\n", rt); -#endif - cdr.Ctrl = rt | (cdr.Ctrl & ~0x3); + CDR_LOG_IO("cdr w0: %02x\n", rt); - if (rt == 0) { - cdr.ParamP = 0; - cdr.ParamC = 0; - cdr.ResultReady = 0; - } + cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3); } unsigned char cdrRead1(void) { - if (cdr.ResultReady) { // && cdr.Ctrl & 0x1) { - psxHu8(0x1801) = cdr.Result[cdr.ResultP++]; - if (cdr.ResultP == cdr.ResultC) - cdr.ResultReady = 0; - } else { + if ((cdr.ResultP & 0xf) < cdr.ResultC) + psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf]; + else psxHu8(0x1801) = 0; - } -#ifdef CDR_LOG - CDR_LOG("cdrRead1() Log: CD1 Read: %x\n", psxHu8(0x1801)); -#endif + cdr.ResultP++; + if (cdr.ResultP == cdr.ResultC) + cdr.ResultReady = 0; + + CDR_LOG_IO("cdr r1: %02x\n", psxHu8(0x1801)); + return psxHu8(0x1801); } void cdrWrite1(unsigned char rt) { + u8 set_loc[3]; int i; -#ifdef CDR_LOG - CDR_LOG("cdrWrite1() Log: CD1 write: %x (%s)\n", rt, CmdName[rt]); -#endif -// psxHu8(0x1801) = rt; - cdr.Cmd = rt; + CDR_LOG_IO("cdr w1: %02x\n", rt); + + switch (cdr.Ctrl & 3) { + case 0: + break; + case 3: + cdr.AttenuatorRightToRightT = rt; + return; + default: + return; + } + + cdr.Cmd = rt; cdr.OCUP = 0; -#ifdef CDRCMD_DEBUG - SysPrintf("cdrWrite1() Log: CD1 write: %x (%s)", rt, CmdName[rt]); +#ifdef CDR_LOG_CMD_IRQ + SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]); if (cdr.ParamC) { SysPrintf(" Param[%d] = {", cdr.ParamC); for (i = 0; i < cdr.ParamC; i++) @@ -770,223 +1224,50 @@ void cdrWrite1(unsigned char rt) { } #endif - if (cdr.Ctrl & 0x1) return; - - switch (cdr.Cmd) { - case CdlSync: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlNop: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlSetloc: - StopReading(); - cdr.Seeked = FALSE; - for (i = 0; i < 3; i++) - cdr.SetSector[i] = btoi(cdr.Param[i]); - cdr.SetSector[3] = 0; -/* if ((cdr.SetSector[0] | cdr.SetSector[1] | cdr.SetSector[2]) == 0) { - *(u32 *)cdr.SetSector = *(u32 *)cdr.SetSectorSeek; - }*/ - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlPlay: - if (!cdr.SetSector[0] & !cdr.SetSector[1] & !cdr.SetSector[2]) { - if (CDR_getTN(cdr.ResultTN) != -1) { - if (cdr.CurTrack > cdr.ResultTN[1]) - cdr.CurTrack = cdr.ResultTN[1]; - if (CDR_getTD((unsigned char)(cdr.CurTrack), cdr.ResultTD) != -1) { - int tmp = cdr.ResultTD[2]; - cdr.ResultTD[2] = cdr.ResultTD[0]; - cdr.ResultTD[0] = tmp; - if (!Config.Cdda) CDR_play(cdr.ResultTD); - } - } - } else if (!Config.Cdda) { - CDR_play(cdr.SetSector); - } - cdr.Play = TRUE; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlForward: - if (cdr.CurTrack < 0xaa) - cdr.CurTrack++; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlBackward: - if (cdr.CurTrack > 1) - cdr.CurTrack--; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlReadN: - cdr.Irq = 0; - StopReading(); - cdr.Ctrl|= 0x80; - cdr.Stat = NoIntr; - StartReading(1, 0x1000); - break; - - case CdlStandby: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlStop: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlPause: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x80000); - break; - - case CdlReset: - case CdlInit: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlMute: - cdr.Muted = TRUE; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlDemute: - cdr.Muted = FALSE; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlSetfilter: - cdr.File = cdr.Param[0]; - cdr.Channel = cdr.Param[1]; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + cdr.ResultReady = 0; + cdr.Ctrl |= 0x80; + // cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + + switch (cdr.Cmd) { + case CdlSetloc: + 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: -#ifdef CDR_LOG - CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]); -#endif - cdr.Mode = cdr.Param[0]; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlGetmode: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlGetlocL: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlGetlocP: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlGetTN: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlGetTD: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlSeekL: -// ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0]; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]); - case CdlSeekP: -// ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0]; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlTest: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlID: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlReadS: - cdr.Irq = 0; - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - StartReading(2, 0x1000); - break; + cdr.Mode = cdr.Param[0]; - case CdlReadToc: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + // Squaresoft on PlayStation 1998 Collector's CD Vol. 1 + // - fixes choppy movie sound + if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 ) + StopCdda(); break; - - default: -#ifdef CDR_LOG - CDR_LOG("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd); -#endif - return; - } - if (cdr.Stat != NoIntr) { - psxHu32ref(0x1070) |= SWAP32((u32)0x4); } } @@ -996,80 +1277,83 @@ unsigned char cdrRead2(void) { if (cdr.Readed == 0) { ret = 0; } else { - ret = *cdr.pTransfer++; + ret = *pTransfer++; } -#ifdef CDR_LOG - CDR_LOG("cdrRead2() Log: CD2 Read: %x\n", ret); -#endif + CDR_LOG_IO("cdr r2: %02x\n", ret); return ret; } void cdrWrite2(unsigned char rt) { -#ifdef CDR_LOG - CDR_LOG("cdrWrite2() Log: CD2 write: %x\n", rt); -#endif - if (cdr.Ctrl & 0x1) { - switch (rt) { - case 0x07: - cdr.ParamP = 0; - cdr.ParamC = 0; - cdr.ResultReady = 1; //0; - cdr.Ctrl &= ~3; //cdr.Ctrl = 0; - break; + CDR_LOG_IO("cdr w2: %02x\n", rt); - default: - cdr.Reg2 = rt; - break; - } - } else if (!(cdr.Ctrl & 0x1) && cdr.ParamP < 8) { - cdr.Param[cdr.ParamP++] = rt; - cdr.ParamC++; + switch (cdr.Ctrl & 3) { + case 0: + if (cdr.ParamC < 8) // FIXME: size and wrapping + cdr.Param[cdr.ParamC++] = rt; + return; + case 1: + cdr.Reg2 = rt; + setIrq(); + return; + case 2: + cdr.AttenuatorLeftToLeftT = rt; + return; + case 3: + cdr.AttenuatorRightToLeftT = rt; + return; } } unsigned char cdrRead3(void) { - if (cdr.Stat) { - if (cdr.Ctrl & 0x1) - psxHu8(0x1803) = cdr.Stat | 0xE0; - else - psxHu8(0x1803) = 0xff; - } else { - psxHu8(0x1803) = 0; - } -#ifdef CDR_LOG - CDR_LOG("cdrRead3() Log: CD3 Read: %x\n", psxHu8(0x1803)); -#endif + if (cdr.Ctrl & 0x1) + psxHu8(0x1803) = cdr.Stat | 0xE0; + else + psxHu8(0x1803) = cdr.Reg2 | 0xE0; + + CDR_LOG_IO("cdr r3: %02x\n", psxHu8(0x1803)); return psxHu8(0x1803); } void cdrWrite3(unsigned char rt) { -#ifdef CDR_LOG - CDR_LOG("cdrWrite3() Log: CD3 write: %x\n", rt); -#endif - if (rt == 0x07 && cdr.Ctrl & 0x1) { - cdr.Stat = 0; + CDR_LOG_IO("cdr w3: %02x\n", rt); - if (cdr.Irq == 0xff) { - cdr.Irq = 0; - return; - } - if (cdr.Irq) - CDR_INT(cdr.eCycle); - if (cdr.Reading && !cdr.ResultReady) - CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); + switch (cdr.Ctrl & 3) { + case 0: + break; // transfer + case 1: + cdr.Stat &= ~rt; + if (rt & 0x40) + cdr.ParamC = 0; + return; + case 2: + cdr.AttenuatorLeftToRightT = rt; + return; + case 3: + if (rt & 0x20) { + memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4); + CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", + cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight, + cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight); + } return; } - if (rt == 0x80 && !(cdr.Ctrl & 0x1) && cdr.Readed == 0) { + + if ((rt & 0x80) && cdr.Readed == 0) { cdr.Readed = 1; - cdr.pTransfer = cdr.Transfer; + pTransfer = cdr.Transfer; switch (cdr.Mode & 0x30) { - case 0x10: + case MODE_SIZE_2328: case 0x00: - cdr.pTransfer += 12; + pTransfer += 12; break; + + case MODE_SIZE_2340: + pTransfer += 0; + break; + default: break; } @@ -1078,39 +1362,69 @@ void cdrWrite3(unsigned char rt) { void psxDma3(u32 madr, u32 bcr, u32 chcr) { u32 cdsize; + int size; u8 *ptr; -#ifdef CDR_LOG CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr); -#endif switch (chcr) { case 0x11000000: case 0x11400100: if (cdr.Readed == 0) { -#ifdef CDR_LOG CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n"); -#endif break; } cdsize = (bcr & 0xffff) * 4; + // Ape Escape: bcr = 0001 / 0000 + // - fix boot + if( cdsize == 0 ) + { + switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) { + case MODE_SIZE_2340: cdsize = 2340; break; + case MODE_SIZE_2328: cdsize = 2328; break; + default: + case MODE_SIZE_2048: cdsize = 2048; break; + } + } + + ptr = (u8 *)PSXM(madr); if (ptr == NULL) { -#ifdef CPU_LOG CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n"); -#endif break; } - memcpy(ptr, cdr.pTransfer, cdsize); + + /* + GS CDX: Enhancement CD crash + - Setloc 0:0:0 + - CdlPlay + - Spams DMA3 and gets buffer overrun + */ + size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer); + if (size > cdsize) + size = cdsize; + if (size > 0) + { + memcpy(ptr, pTransfer, size); + } + psxCpu->Clear(madr, cdsize / 4); - cdr.pTransfer += cdsize; - break; + pTransfer += cdsize; + + + // burst vs normal + if( chcr == 0x11400100 ) { + CDRDMA_INT( (cdsize/4) / 4 ); + } + else if( chcr == 0x11000000 ) { + CDRDMA_INT( (cdsize/4) * 1 ); + } + return; + default: -#ifdef CDR_LOG CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr); -#endif break; } @@ -1118,26 +1432,96 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) { DMA_INTERRUPT(3); } +void cdrDmaInterrupt() +{ + if (HW_DMA3_CHCR & SWAP32(0x01000000)) + { + HW_DMA3_CHCR &= SWAP32(~0x01000000); + DMA_INTERRUPT(3); + } +} + +static void getCdInfo(void) +{ + u8 tmp; + + CDR_getTN(cdr.ResultTN); + CDR_getTD(0, cdr.SetSectorEnd); + tmp = cdr.SetSectorEnd[0]; + cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2]; + cdr.SetSectorEnd[2] = tmp; +} + void cdrReset() { memset(&cdr, 0, sizeof(cdr)); cdr.CurTrack = 1; cdr.File = 1; cdr.Channel = 1; - memset(&stat, 0, sizeof(stat)); + cdr.Reg2 = 0x1f; + cdr.Stat = NoIntr; + cdr.DriveState = DRIVESTATE_STANDBY; + cdr.StatP = STATUS_ROTATING; + pTransfer = cdr.Transfer; + + // BIOS player - default values + cdr.AttenuatorLeftToLeft = 0x80; + cdr.AttenuatorLeftToRight = 0x00; + cdr.AttenuatorRightToLeft = 0x00; + cdr.AttenuatorRightToRight = 0x80; + + getCdInfo(); } -int cdrFreeze(gzFile f, int Mode) { - uintptr_t tmp; +int cdrFreeze(void *f, int Mode) { + u32 tmp; + u8 tmpp[3]; + if (Mode == 0 && !Config.Cdda) + CDR_stop(); + + cdr.freeze_ver = 0x63647202; gzfreeze(&cdr, sizeof(cdr)); - - if (Mode == 1) - tmp = cdr.pTransfer - cdr.Transfer; + + if (Mode == 1) { + cdr.ParamP = cdr.ParamC; + tmp = pTransfer - cdr.Transfer; + } gzfreeze(&tmp, sizeof(tmp)); - if (Mode == 0) - cdr.pTransfer = cdr.Transfer + tmp; + if (Mode == 0) { + getCdInfo(); + + pTransfer = cdr.Transfer + tmp; + + // read right sub data + memcpy(tmpp, cdr.Prev, 3); + cdr.Prev[0]++; + ReadTrack(tmpp); + + if (cdr.Play) { + if (cdr.freeze_ver < 0x63647202) + memcpy(cdr.SetSectorPlay, cdr.SetSector, 3); + + Find_CurTrack(cdr.SetSectorPlay); + if (!Config.Cdda) + CDR_play(cdr.SetSectorPlay); + } + + if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) { + // old versions did not latch Reg2, have to fixup.. + if (cdr.Reg2 == 0) { + SysPrintf("cdrom: fixing up old savestate\n"); + cdr.Reg2 = 7; + } + } + } return 0; } + +void LidInterrupt() { + getCdInfo(); + StopCdda(); + cdrLidSeekInterrupt(); +}