#include "psxdma.h"
cdrStruct cdr;
+static unsigned char *pTransfer;
/* CD-ROM magic numbers */
#define CdlSync 0
// so (PSXCLK / 75) = cdr read time (linuzappz)
#define cdReadTime (PSXCLK / 75)
+// for cdr.Seeked
+enum seeked_state {
+ SEEK_PENDING = 0,
+ SEEK_DONE = 1,
+ SEEK_DOING_CMD = 2,
+};
+
static struct CdrStat stat;
-static unsigned int msf2sec(char *msf) {
+static unsigned int msf2sec(u8 *msf) {
return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
}
-static void sec2msf(unsigned int s, char *msf) {
+static void sec2msf(unsigned int s, u8 *msf) {
msf[0] = s / 75 / 60;
s = s - msf[0] * 75 * 60;
msf[1] = s / 75;
msf[2] = s;
}
-
+// cdrInterrupt
#define CDR_INT(eCycle) { \
psxRegs.interrupt |= (1 << PSXINT_CDR); \
psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
new_dyna_set_event(PSXINT_CDR, eCycle); \
}
+// cdrReadInterrupt
#define CDREAD_INT(eCycle) { \
psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \
new_dyna_set_event(PSXINT_CDREAD, eCycle); \
}
+// cdrLidSeekInterrupt
#define CDRLID_INT(eCycle) { \
psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
new_dyna_set_event(PSXINT_CDRLID, eCycle); \
}
-#define CDRPLAY_INT(eCycle) { \
+// cdrPlayInterrupt
+#define CDRMISC_INT(eCycle) { \
psxRegs.interrupt |= (1 << PSXINT_CDRPLAY); \
psxRegs.intCycle[PSXINT_CDRPLAY].cycle = eCycle; \
psxRegs.intCycle[PSXINT_CDRPLAY].sCycle = psxRegs.cycle; \
}
}
-
static void Check_Shell( int Irq )
{
// check case open/close
}
-void AddIrqQueue(unsigned char irq, unsigned long ecycle) {
+static void AddIrqQueue(unsigned char irq, unsigned long ecycle) {
+ //if (cdr.Irq != 0 && cdr.Irq != 0xff)
+ // printf("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq);
+
cdr.Irq = irq;
cdr.eCycle = ecycle;
- // Doom: Force rescheduling
- // - Fixes boot
CDR_INT(ecycle);
}
static void cdrPlayInterrupt_Autopause()
{
struct SubQ *subq = (struct SubQ *)CDR_getBufferSub();
+ int track_changed = 0;
if (subq != NULL ) {
+ // update subq
+ ReadTrack( cdr.SetSectorPlay );
+
#ifdef CDR_LOG
CDR_LOG( "CDDA SUB - %X:%X:%X\n",
subq->AbsoluteAddress[0], subq->AbsoluteAddress[1], subq->AbsoluteAddress[2] );
Tomb Raider 1 ($7)
*/
- if( cdr.CurTrack >= btoi( subq->TrackNumber ) )
- return;
+ // .. + 1 is probably wrong, but deals with corrupted subq + good checksum
+ // (how does real thing handle those?)
+ if( cdr.CurTrack + 1 == btoi( subq->TrackNumber ) )
+ track_changed = 1;
} else {
Create_Fake_Subq();
#ifdef CDR_LOG___0
fake_subq_real[0], fake_subq_real[1], fake_subq_real[2] );
#endif
- if( !fake_subq_change )
- return;
-
+ track_changed = fake_subq_change;
fake_subq_change = 0;
}
- if (cdr.Mode & MODE_AUTOPAUSE) {
+ if ((cdr.Mode & MODE_AUTOPAUSE) && track_changed) {
#ifdef CDR_LOG
CDR_LOG( "CDDA STOP\n" );
#endif
StopCdda();
}
- if (cdr.Mode & MODE_REPORT) {
- // rearmed note: PCSX-Reloaded does this for every sector,
- // but we try to get away with only track change here.
- memset( cdr.Result, 0, 8 );
- cdr.Result[0] |= 0x10;
-
+ else if (cdr.Mode & MODE_REPORT) {
if (subq != NULL) {
#ifdef CDR_LOG
CDR_LOG( "REPPLAY SUB - %X:%X:%X\n",
subq->AbsoluteAddress[0], subq->AbsoluteAddress[1], subq->AbsoluteAddress[2] );
#endif
- cdr.CurTrack = btoi( subq->TrackNumber );
+ // breaks when .sub doesn't have index 0 for some reason (bad rip?)
+ //cdr.CurTrack = btoi( subq->TrackNumber );
+
+ if (subq->AbsoluteAddress[2] & 0xf)
+ return;
+ cdr.Result[0] = cdr.StatP;
// BIOS CD Player: data already BCD format
cdr.Result[1] = subq->TrackNumber;
cdr.Result[2] = subq->IndexNumber;
fake_subq_real[0], fake_subq_real[1], fake_subq_real[2] );
#endif
+ if (fake_subq_real[2] & 0xf)
+ return;
+
+ cdr.Result[0] = cdr.StatP;
// track # / index #
cdr.Result[1] = itob(cdr.CurTrack);
cdr.Result[2] = itob(fake_subq_index);
}
}
+// also handles seek
void cdrPlayInterrupt()
{
- if( !cdr.Play ) return;
+ if (cdr.Seeked == SEEK_DOING_CMD) {
+ SetResultSize(1);
+ cdr.StatP |= STATUS_ROTATING;
+ cdr.StatP &= ~STATUS_SEEK;
+ cdr.Result[0] = cdr.StatP;
+ if (cdr.Irq == 0 || cdr.Irq == 0xff) {
+ cdr.Stat = Complete;
+ if (cdr.Stat != NoIntr)
+ psxHu32ref(0x1070) |= SWAP32(0x4);
+ }
+
+ cdr.Seeked = SEEK_PENDING;
+ CDRMISC_INT(cdReadTime * 20); // ???
+ return;
+ }
+ else if (cdr.Seeked == SEEK_PENDING) {
+ cdr.Seeked = SEEK_DONE;
+ if (!cdr.Play && !cdr.Reading) {
+ memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
+ Find_CurTrack();
+ ReadTrack(cdr.SetSector);
+ }
+ }
+
+ if (!cdr.Play) return;
#ifdef CDR_LOG
CDR_LOG( "CDDA - %d:%d:%d\n",
cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
#endif
- CDRPLAY_INT( cdReadTime );
+ CDRMISC_INT( cdReadTime );
if (!cdr.Irq && !cdr.Stat && (cdr.Mode & MODE_CDDA) && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
cdrPlayInterrupt_Autopause();
case CdlPlay:
fake_subq_change = 0;
- if( cdr.Seeked == FALSE ) {
+ if (cdr.Seeked == SEEK_PENDING) {
+ // XXX: wrong, should seek instead..
memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 );
- cdr.Seeked = TRUE;
+ cdr.Seeked = SEEK_DONE;
}
/*
cdr.SetSectorPlay[2] = cdr.ResultTD[0];
// reset data
- Set_Track();
+ //Set_Track();
Find_CurTrack();
ReadTrack( cdr.SetSectorPlay );
// BIOS player - set flag again
cdr.Play = TRUE;
- CDRPLAY_INT( cdReadTime );
+ CDRMISC_INT( cdReadTime );
break;
case CdlForward:
subq = (struct SubQ *)CDR_getBufferSub();
if (subq != NULL) {
+ if( cdr.Play && (cdr.Mode & MODE_CDDA) && !(cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)) )
+ // update subq
+ ReadTrack( cdr.SetSectorPlay );
+
cdr.Result[0] = subq->TrackNumber;
cdr.Result[1] = subq->IndexNumber;
memcpy(cdr.Result + 2, subq->TrackRelativeAddress, 3);
break;
case CdlSeekL:
+ case CdlSeekP:
SetResultSize(1);
cdr.StatP |= STATUS_ROTATING;
cdr.Result[0] = cdr.StatP;
Rockman X5 = 0.5-4x
- fix capcom logo
*/
- AddIrqQueue(CdlSeekL + 0x20, cdReadTime * 4);
- break;
-
- case CdlSeekL + 0x20:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.StatP &= ~STATUS_SEEK;
- cdr.Result[0] = cdr.StatP;
- cdr.Seeked = TRUE;
- cdr.Stat = Complete;
-
-
- // Mega Man Legends 2: must update read cursor for getlocp
- ReadTrack( cdr.SetSector );
- break;
-
- case CdlSeekP:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.StatP |= STATUS_SEEK;
- cdr.Stat = Acknowledge;
- AddIrqQueue(CdlSeekP + 0x20, cdReadTime * 1);
- break;
-
- case CdlSeekP + 0x20:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.StatP &= ~STATUS_SEEK;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Complete;
- cdr.Seeked = TRUE;
-
- // GameShark Music Player
- memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 );
-
- // Tomb Raider 2: must update read cursor for getlocp
- Find_CurTrack();
- ReadTrack( cdr.SetSectorPlay );
+ CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4);
+ cdr.Seeked = SEEK_DOING_CMD;
break;
case CdlTest:
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) {
- cdr.Seeked = TRUE;
-
+ if (cdr.Seeked != SEEK_DONE) {
cdr.StatP |= STATUS_SEEK;
cdr.StatP &= ~STATUS_READ;
Check_Shell( Irq );
+ cdr.ParamP = 0;
+ cdr.ParamC = 0;
+
if (cdr.Stat != NoIntr && cdr.Reg2 != 0x18) {
psxHu32ref(0x1070) |= SWAP32((u32)0x4);
}
#ifdef CDR_LOG
- CDR_LOG("cdrInterrupt() Log: CDR Interrupt IRQ %x\n", Irq);
+ printf("cdrInterrupt() Log: CDR Interrupt IRQ %x: ", Irq);
+ for (i = 0; i < cdr.ResultC; i++)
+ printf("%02x ", cdr.Result[i]);
+ printf("\n");
#endif
}
cdr.StatP |= STATUS_READ|STATUS_ROTATING;
cdr.StatP &= ~STATUS_SEEK;
cdr.Result[0] = cdr.StatP;
+ cdr.Seeked = SEEK_DONE;
ReadTrack( cdr.SetSector );
int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
if (!ret) {
+ // only handle attenuator basic channel switch for now
+ if (cdr.Xa.stereo) {
+ int i;
+ if ((cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
+ && !(cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
+ {
+ for (i = 0; i < cdr.Xa.nsamples; i++)
+ cdr.Xa.pcm[i*2 + 1] = cdr.Xa.pcm[i*2];
+ }
+ else if (!(cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
+ && (cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
+ {
+ for (i = 0; i < cdr.Xa.nsamples; i++)
+ cdr.Xa.pcm[i*2] = cdr.Xa.pcm[i*2 + 1];
+ }
+ }
+
SPU_playADPCMchannel(&cdr.Xa);
cdr.FirstSector = 0;
#ifdef CDR_LOG
CDR_LOG("cdrWrite0() Log: CD0 write: %x\n", rt);
#endif
- cdr.Ctrl = rt | (cdr.Ctrl & ~0x3);
-
- if (rt == 0) {
- cdr.ParamP = 0;
- cdr.ParamC = 0;
- cdr.ResultReady = 0;
- }
+ cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
}
unsigned char cdrRead1(void) {
}
void cdrWrite1(unsigned char rt) {
+ u8 set_loc[3];
int i;
#ifdef CDR_LOG
// Tekken: CDXA fade-out
if( (cdr.Ctrl & 3) == 3 ) {
- //cdr.AttenuatorRight[0] = rt;
+ cdr.AttenuatorRight[0] = rt;
}
}
#endif
- if (cdr.Ctrl & 0x1) return;
+ if (cdr.Ctrl & 0x3) return;
+
+ cdr.ResultReady = 0;
switch (cdr.Cmd) {
case CdlSync:
case CdlSetloc:
StopReading();
- cdr.Seeked = FALSE;
for (i = 0; i < 3; i++)
- cdr.SetSector[i] = btoi(cdr.Param[i]);
- cdr.SetSector[3] = 0;
+ set_loc[i] = btoi(cdr.Param[i]);
- /*
- if ((cdr.SetSector[0] | cdr.SetSector[1] | cdr.SetSector[2]) == 0) {
- *(u32 *)cdr.SetSector = *(u32 *)cdr.SetSectorSeek;
- }*/
+ i = abs(msf2sec(cdr.SetSector) - msf2sec(set_loc));
+ if (i > 16)
+ cdr.Seeked = SEEK_PENDING;
+
+ memcpy(cdr.SetSector, set_loc, 3);
+ cdr.SetSector[3] = 0;
cdr.Ctrl |= 0x80;
cdr.Stat = NoIntr;
case CdlReset:
case CdlInit:
+ cdr.Seeked = SEEK_DONE;
StopCdda();
StopReading();
cdr.Ctrl |= 0x80;
break;
default:
+ cdr.ParamP = 0;
+ cdr.ParamC = 0;
#ifdef CDR_LOG
CDR_LOG("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd);
#endif
if (cdr.Readed == 0) {
ret = 0;
} else {
- ret = *cdr.pTransfer++;
+ ret = *pTransfer++;
}
#ifdef CDR_LOG
// Tekken: CDXA fade-out
if( (cdr.Ctrl & 3) == 2 ) {
- //cdr.AttenuatorLeft[0] = rt;
+ cdr.AttenuatorLeft[0] = rt;
}
else if( (cdr.Ctrl & 3) == 3 ) {
- //cdr.AttenuatorRight[1] = rt;
+ cdr.AttenuatorRight[1] = rt;
}
cdr.Reg2 = rt;
break;
}
- } else if (!(cdr.Ctrl & 0x1) && cdr.ParamP < 8) {
+ } else if (!(cdr.Ctrl & 0x3) && cdr.ParamP < 8) {
cdr.Param[cdr.ParamP++] = rt;
cdr.ParamC++;
}
#ifdef CDR_LOG
CDR_LOG("cdrWrite3() Log: CD3 write: %x\n", rt);
#endif
-/*
+
// Tekken: CDXA fade-out
if( (cdr.Ctrl & 3) == 2 ) {
cdr.AttenuatorLeft[1] = rt;
cdr.AttenuatorRight[0], cdr.AttenuatorRight[1] );
#endif
}
-*/
+
// GameShark CDX CD Player: Irq timing mania
if( rt == 0 &&
}
- if (rt == 0x07 && cdr.Ctrl & 0x1) {
+ if (rt == 0x07 && (cdr.Ctrl & 3) == 1) {
cdr.Stat = 0;
if (cdr.Irq == 0xff) {
// - Final Fantasy Tactics
// - various other games
- if (cdr.Irq) // rearmed guesswork hack
if (cdr.Reading && !cdr.ResultReady) {
- CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
+ int left = psxRegs.intCycle[PSXINT_CDREAD].sCycle + psxRegs.intCycle[PSXINT_CDREAD].cycle - psxRegs.cycle;
+ int time = (cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime;
+ if (Config.CdrReschedule != 2)
+ if (left < time / 2 || Config.CdrReschedule) { // rearmed guesswork hack
+ //printf("-- resched %d -> %d\n", left, time);
+ CDREAD_INT(time);
+ }
}
return;
}
- if (rt == 0x80 && !(cdr.Ctrl & 0x1) && cdr.Readed == 0) {
+ if (rt == 0x80 && !(cdr.Ctrl & 0x3) && cdr.Readed == 0) {
cdr.Readed = 1;
- cdr.pTransfer = cdr.Transfer;
+ pTransfer = cdr.Transfer;
switch (cdr.Mode & 0x30) {
case MODE_SIZE_2328:
case 0x00:
- cdr.pTransfer += 12;
+ pTransfer += 12;
break;
case MODE_SIZE_2340:
- cdr.pTransfer += 0;
+ pTransfer += 0;
break;
default:
void psxDma3(u32 madr, u32 bcr, u32 chcr) {
u32 cdsize;
+ int size;
u8 *ptr;
#ifdef CDR_LOG
- CdlPlay
- Spams DMA3 and gets buffer overrun
*/
-
- if( (cdr.pTransfer-cdr.Transfer) + cdsize > 2352 )
- {
- // avoid crash - probably should wrap here
- //memcpy(ptr, cdr.pTransfer, cdsize);
- }
- else
+ size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
+ if (size > cdsize)
+ size = cdsize;
+ if (size > 0)
{
- memcpy(ptr, cdr.pTransfer, cdsize);
+ memcpy(ptr, pTransfer, size);
}
psxCpu->Clear(madr, cdsize / 4);
- cdr.pTransfer += cdsize;
+ pTransfer += cdsize;
// burst vs normal
void cdrDmaInterrupt()
{
- HW_DMA3_CHCR &= SWAP32(~0x01000000);
- DMA_INTERRUPT(3);
+ if (HW_DMA3_CHCR & SWAP32(0x01000000))
+ {
+ HW_DMA3_CHCR &= SWAP32(~0x01000000);
+ DMA_INTERRUPT(3);
+ }
}
void cdrReset() {
cdr.CurTrack = 1;
cdr.File = 1;
cdr.Channel = 1;
+ pTransfer = cdr.Transfer;
+
+ // BIOS player - default values
+ cdr.AttenuatorLeft[0] = 0x80;
+ cdr.AttenuatorLeft[1] = 0x00;
+ cdr.AttenuatorRight[0] = 0x80;
+ cdr.AttenuatorRight[1] = 0x00;
}
int cdrFreeze(gzFile f, int Mode) {
- uintptr_t tmp;
-
+ u32 tmp;
if( Mode == 0 ) {
StopCdda();
gzfreeze(&cdr, sizeof(cdr));
if (Mode == 1)
- tmp = cdr.pTransfer - cdr.Transfer;
+ tmp = pTransfer - cdr.Transfer;
gzfreeze(&tmp, sizeof(tmp));
- if (Mode == 0)
- cdr.pTransfer = cdr.Transfer + tmp;
+ if (Mode == 0) {
+ pTransfer = cdr.Transfer + tmp;
+
+ if (cdr.Play && !Config.Cdda)
+ CDR_play(cdr.SetSectorPlay);
+ }
return 0;
}