* 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.
*/
#define CdlID 26
#define CdlReadS 27
#define CdlReset 28
+#define CdlGetQ 29
#define CdlReadToc 30
char *CmdName[0x100]= {
#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 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,
cdr.Reading = 0; \
psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
} \
- cdr.StatP &= ~STATUS_READ;\
+ cdr.StatP &= ~(STATUS_READ|STATUS_SEEK);\
}
#define StopCdda() { \
psxHu32ref(0x1070) |= SWAP32((u32)0x4);
}
+// 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()
{
- // turn back on checking
- if( cdr.LidCheck == 0x10 )
- {
- cdr.LidCheck = 0;
- }
-
- // official lid close
- else if( cdr.LidCheck == 0x30 )
- {
- // GS CDX 3.3: $13
- cdr.StatP |= STATUS_ROTATING;
-
-
- // GS CDX 3.3 - ~50 getlocp tries
- CDRLID_INT( cdReadTime * 3 );
- cdr.LidCheck = 0x40;
- }
-
- // turn off ready
- else if( cdr.LidCheck == 0x40 )
- {
- // GS CDX 3.3: $01
- cdr.StatP &= ~STATUS_SHELLOPEN;
- cdr.StatP &= ~STATUS_ROTATING;
-
-
- // GS CDX 3.3 - ~50 getlocp tries
- CDRLID_INT( cdReadTime * 3 );
- cdr.LidCheck = 0x50;
- }
-
- // now seek
- else if( cdr.LidCheck == 0x50 )
- {
- // GameShark Lite: Start seeking ($42)
- cdr.StatP |= STATUS_SEEK;
- cdr.StatP |= STATUS_ROTATING;
- cdr.StatP &= ~STATUS_ERROR;
-
-
- CDRLID_INT( cdReadTime * 3 );
- cdr.LidCheck = 0x60;
- }
-
- // done = cd ready
- else if( cdr.LidCheck == 0x60 )
- {
- // GameShark Lite: Seek detection done ($02)
+ switch (cdr.DriveState) {
+ default:
+ case DRIVESTATE_STANDBY:
cdr.StatP &= ~STATUS_SEEK;
- cdr.LidCheck = 0;
- }
-}
-
-static void Check_Shell( int Irq )
-{
- // check case open/close
- if (cdr.LidCheck > 0)
- {
- CDR_LOG( "LidCheck\n" );
+ if (CDR_getStatus(&stat) == -1)
+ return;
- // $20 = check lid state
- if( cdr.LidCheck == 0x20 )
+ if (stat.Status & STATUS_SHELLOPEN)
{
- u32 i;
-
- i = stat.Status;
- if (CDR_getStatus(&stat) != -1)
- {
- // BIOS hangs + BIOS error messages
- //if (stat.Type == 0xff)
- //cdr.Stat = DiskError;
-
- // case now open
- if (stat.Status & STATUS_SHELLOPEN)
- {
- // Vib Ribbon: pre-CD swap
- StopCdda();
-
-
- // GameShark Lite: Death if DiskError happens
- //
- // Vib Ribbon: Needs DiskError for CD swap
-
- if (Irq != CdlNop)
- {
- cdr.Stat = DiskError;
-
- cdr.StatP |= STATUS_ERROR;
- cdr.Result[0] |= STATUS_ERROR;
- }
-
- // GameShark Lite: Wants -exactly- $10
- cdr.StatP |= STATUS_SHELLOPEN;
- cdr.StatP &= ~STATUS_ROTATING;
-
-
- CDRLID_INT( cdReadTime * 3 );
- cdr.LidCheck = 0x10;
-
-
- // GS CDX 3.3 = $11
- }
-
- // case just closed
- else if ( i & STATUS_SHELLOPEN )
- {
- cdr.StatP |= STATUS_ROTATING;
-
- CheckCdrom();
-
-
- if( cdr.Stat == NoIntr )
- cdr.Stat = Acknowledge;
-
- setIrq();
+ StopCdda();
+ cdr.DriveState = DRIVESTATE_LID_OPEN;
+ CDRLID_INT(0x800);
+ }
+ break;
- // begin close-seek-ready cycle
- CDRLID_INT( cdReadTime * 3 );
- cdr.LidCheck = 0x30;
+ 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;
- // GameShark Lite: Wants -exactly- $42, then $02
- // GS CDX 3.3: Wants $11/$80, $13/$80, $01/$00
- }
+ // could generate error irq here, but real hardware
+ // only sometimes does that
+ // (not done when lots of commands are sent?)
- // case still closed - wait for recheck
- else
- {
- CDRLID_INT( cdReadTime * 3 );
- cdr.LidCheck = 0x10;
- }
- }
+ 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
- // GS CDX: clear all values but #1,#2
- if( (cdr.LidCheck >= 0x30) || (cdr.StatP & STATUS_SHELLOPEN) )
- {
- SetResultSize(16);
- memset( cdr.Result, 0, 16 );
-
- cdr.Result[0] = cdr.StatP;
+ cdr.DriveState = DRIVESTATE_RESCAN_CD;
+ CDRLID_INT(cdReadTime * 105);
+ break;
+ }
+ // recheck for close
+ CDRLID_INT(cdReadTime * 3);
+ break;
- // GS CDX: special return value
- if( cdr.StatP & STATUS_SHELLOPEN )
- {
- cdr.Result[1] = 0x80;
- }
+ 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;
- if( cdr.Stat == NoIntr )
- cdr.Stat = Acknowledge;
+ case DRIVESTATE_PREPARE_CD:
+ cdr.StatP |= STATUS_SEEK;
- setIrq();
- }
+ cdr.DriveState = DRIVESTATE_STANDBY;
+ CDRLID_INT(cdReadTime * 26);
+ 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;
cdr.RErr = CDR_readTrack(tmp);
memcpy(cdr.Prev, tmp, 3);
- cdr.TrackChanged = FALSE;
-
if (CheckSBI(time))
return;
subq = (struct SubQ *)CDR_getBufferSub();
- if (subq != NULL) {
+ 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);
-
- // .. + 1 is probably wrong, but deals with corrupted
- // subq + good checksum
- // (how does real thing handle that?)
- if (cdr.CurTrack + 1 == btoi(subq->TrackNumber)) {
- cdr.CurTrack++;
- cdr.TrackChanged = TRUE;
- }
- }
- }
- else {
- 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);
-
- if (next_s - this_s < pregap) {
- cdr.TrackChanged = TRUE;
- cdr.CurTrack++;
- start_s = next_s;
+ CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
+ tmp[0], tmp[1], tmp[2]);
}
-
- 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] = tmp[0];
- cdr.subq.Absolute[1] = tmp[1];
- cdr.subq.Absolute[2] = tmp[2];
+ }
+ else {
+ generate_subq(time);
}
CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
}
-static void AddIrqQueue(unsigned char irq, unsigned long ecycle) {
- if (cdr.Irq != 0)
+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;
setIrq();
}
- memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
+ if (cdr.SetlocPending) {
+ memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
+ cdr.SetlocPending = 0;
+ }
Find_CurTrack(cdr.SetSectorPlay);
ReadTrack(cdr.SetSectorPlay);
cdr.TrackChanged = FALSE;
CDRMISC_INT(cdReadTime);
// update for CdlGetlocP/autopause
- ReadTrack(cdr.SetSectorPlay);
+ 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) {
return;
}
- cdr.Irq = 0;
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 |= STATUS_ROTATING;
- 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;
-
- if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
+ if (cdr.DriveState != DRIVESTATE_LID_OPEN)
+ cdr.StatP &= ~STATUS_SHELLOPEN;
+ no_busy_error = 1;
break;
case CdlSetloc:
- cdr.CmdProcess = 0;
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
break;
+ do_CdlPlay:
case CdlPlay:
+ StopCdda();
if (cdr.Seeked == SEEK_PENDING) {
// XXX: wrong, should seek instead..
- memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 );
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!!)
// Vib Ribbon: gameplay checks flag
cdr.StatP &= ~STATUS_SEEK;
-
- cdr.CmdProcess = 0;
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
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 |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
+ // 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++;
break;
case CdlBackward:
- cdr.CmdProcess = 0;
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
cdr.Stat = Complete;
-
// GameShark CD Player: Calls 2x + Play 2x
if( cdr.FastBackward == 0 ) cdr.FastBackward = 2;
else cdr.FastBackward++;
break;
case CdlStandby:
- cdr.CmdProcess = 0;
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
+ 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);
+ 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;
-
- if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
break;
case CdlPause:
- SetResultSize(1);
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
-
/*
Gundam Battle Assault 2: much slower (*)
- Fixes boot, gameplay
InuYasha - Feudal Fairy Tale: slower
- Fixes battles
*/
- AddIrqQueue(CdlPause + 0x20, cdReadTime * 3);
+ AddIrqQueue(CdlPause + 0x100, cdReadTime * 3);
cdr.Ctrl |= 0x80;
break;
- case CdlPause + 0x20:
- SetResultSize(1);
+ case CdlPause + 0x100:
cdr.StatP &= ~STATUS_READ;
- cdr.StatP |= STATUS_ROTATING;
cdr.Result[0] = cdr.StatP;
cdr.Stat = Complete;
break;
case CdlInit:
- SetResultSize(1);
- cdr.StatP = STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
-// if (!cdr.Init) {
- AddIrqQueue(CdlInit + 0x20, 0x800);
-// }
- break;
+ AddIrqQueue(CdlInit + 0x100, cdReadTime * 6);
+ no_busy_error = 1;
+ start_rotating = 1;
+ break;
- case CdlInit + 0x20:
- SetResultSize(1);
- cdr.Result[0] = cdr.StatP;
+ case CdlInit + 0x100:
cdr.Stat = Complete;
- cdr.Init = 1;
break;
case CdlMute:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
+ cdr.Muted = TRUE;
break;
case CdlDemute:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
+ cdr.Muted = FALSE;
break;
case CdlSetfilter:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
+ cdr.File = cdr.Param[0];
+ cdr.Channel = cdr.Param[1];
break;
case CdlSetmode:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
+ no_busy_error = 1;
break;
case CdlGetmode:
SetResultSize(6);
- cdr.StatP |= STATUS_ROTATING;
- 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;
+ no_busy_error = 1;
break;
case CdlGetlocL:
SetResultSize(8);
- for (i = 0; i < 8; i++)
- cdr.Result[i] = cdr.Transfer[i];
- cdr.Stat = Acknowledge;
+ memcpy(cdr.Result, cdr.Transfer, 8);
break;
case CdlGetlocP:
if (!cdr.Play && !cdr.Reading)
cdr.Result[1] = 0; // HACK?
+ break;
- cdr.Stat = Acknowledge;
+ case CdlReadT: // SetSession?
+ // really long
+ AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
+ start_rotating = 1;
break;
- case CdlGetTN:
- // 5-Star Racing: don't stop CDDA
- //
- // Vib Ribbon: CD swap
- StopReading();
+ case CdlReadT + 0x100:
+ cdr.Stat = Complete;
+ break;
- cdr.CmdProcess = 0;
+ case CdlGetTN:
SetResultSize(3);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
if (CDR_getTN(cdr.ResultTN) == -1) {
cdr.Stat = DiskError;
cdr.Result[0] |= STATUS_ERROR;
break;
case CdlGetTD:
- cdr.CmdProcess = 0;
cdr.Track = btoi(cdr.Param[0]);
SetResultSize(4);
- cdr.StatP |= STATUS_ROTATING;
if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
cdr.Stat = DiskError;
cdr.Result[0] |= STATUS_ERROR;
case CdlSeekL:
case CdlSeekP:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
+ StopCdda();
+ StopReading();
cdr.StatP |= STATUS_SEEK;
- cdr.Stat = Acknowledge;
/*
Crusaders of Might and Magic = 0.5x-4x
*/
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
SetResultSize(4);
memcpy(cdr.Result, Test23, 4);
break;
}
+ no_busy_error = 1;
break;
case CdlID:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
- AddIrqQueue(CdlID + 0x20, 0x800);
+ AddIrqQueue(CdlID + 0x100, 20480);
break;
- case CdlID + 0x20:
+ case CdlID + 0x100:
SetResultSize(8);
+ cdr.Result[0] = cdr.StatP;
+ cdr.Result[1] = 0;
+ cdr.Result[2] = 0;
+ cdr.Result[3] = 0;
- if (CDR_getStatus(&stat) == -1) {
- cdr.Result[0] = 0x00; // 0x08 and cdr.Result[1]|0x10 : audio cd, enters cd player
- cdr.Result[1] = 0x80; // 0x80 leads to the menu in the bios, else loads CD
+ // 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) {
- // Music CD
- cdr.Result[0] = 0x08;
- cdr.Result[1] = 0x10;
-
+ if (stat.Type == 2)
+ cdr.Result[1] |= 0x10;
+ if (CdromId[0] == '\0')
cdr.Result[1] |= 0x80;
- }
- else {
- // Data CD
- if (CdromId[0] == '\0') {
- cdr.Result[0] = 0x00;
- cdr.Result[1] = 0x80;
- }
- else {
- cdr.Result[0] = 0x08;
- cdr.Result[1] = 0x00;
- }
- }
}
+ cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
- cdr.Result[2] = 0x00;
- cdr.Result[3] = 0x00;
strncpy((char *)&cdr.Result[4], "PCSX", 4);
cdr.Stat = Complete;
break;
case CdlReset:
- SetResultSize(1);
- cdr.StatP = STATUS_ROTATING;
- 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 CdlReadT:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
- AddIrqQueue(CdlReadT + 0x20, 0x800);
- break;
-
- case CdlReadT + 0x20:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Complete;
+ case CdlGetQ:
+ // TODO?
+ CDR_LOG_I("got CdlGetQ\n");
break;
case CdlReadToc:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
- AddIrqQueue(CdlReadToc + 0x20, 0x800);
+ AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
+ no_busy_error = 1;
+ start_rotating = 1;
break;
- case CdlReadToc + 0x20:
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
- cdr.Result[0] = cdr.StatP;
+ case CdlReadToc + 0x100:
cdr.Stat = Complete;
+ no_busy_error = 1;
break;
case CdlReadN:
case CdlReadS:
- if (!cdr.Reading) return;
+ 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
- cdr.CurTrack = 1;
- ReadTrack(cdr.SetSector);
+ ReadTrack(cdr.SetSectorPlay);
// Crusaders of Might and Magic - update getlocl now
CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
}
- SetResultSize(1);
- cdr.StatP |= STATUS_ROTATING;
cdr.Result[0] = cdr.StatP;
- cdr.Stat = Acknowledge;
+ start_rotating = 1;
break;
default:
- cdr.Stat = Complete;
+ 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;
}
- Check_Shell( Irq );
+ if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
+ cdr.DriveState = DRIVESTATE_STANDBY;
+ cdr.StatP |= STATUS_ROTATING;
+ }
- cdr.ParamC = 0;
+ 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
- SysPrintf("cdrInterrupt() Log: CDR Interrupt IRQ %d %02x: ",
- cdr.Stat != NoIntr && cdr.Reg2 != 0x18, Irq);
- for (i = 0; i < cdr.ResultC; i++)
- SysPrintf("%02x ", cdr.Result[i]);
- SysPrintf("\n");
+ {
+ 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 __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() {
cdr.Result[0] = cdr.StatP;
cdr.Seeked = SEEK_DONE;
- ReadTrack( cdr.SetSector );
+ ReadTrack(cdr.SetSectorPlay);
buf = CDR_getBuffer();
if (buf == NULL)
cdr.Channel = cdr.Transfer[4 + 1];
}
- if ((cdr.Transfer[4 + 2] & 0x4) &&
+ if((cdr.Transfer[4 + 2] & 0x4) &&
(cdr.Transfer[4 + 1] == cdr.Channel) &&
- (cdr.Transfer[4 + 0] == cdr.File)) {
+ (cdr.Transfer[4 + 0] == cdr.File)) {
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];
- }
- }
-
+ cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
SPU_playADPCMchannel(&cdr.Xa);
cdr.FirstSector = 0;
-
-#if 0
- // Crash Team Racing: music, speech
- // - done using cdda decoded buffer (spu irq)
- // - don't do here
-
- // signal ADPCM data ready
- setIrq();
-#endif
}
else cdr.FirstSector = -1;
}
}
- 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.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]++;
}
}
}
// update for CdlGetlocP
- ReadTrack(cdr.SetSector);
-
- Check_Shell(0);
+ ReadTrack(cdr.SetSectorPlay);
}
/*
case 0:
break;
case 3:
- cdr.AttenuatorRight[1] = rt;
+ cdr.AttenuatorRightToRightT = rt;
return;
default:
return;
cdr.OCUP = 0;
#ifdef CDR_LOG_CMD_IRQ
- SysPrintf("cdrWrite1() Log: CD1 write: %x (%s)", rt, CmdName[rt]);
+ SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
if (cdr.ParamC) {
SysPrintf(" Param[%d] = {", cdr.ParamC);
for (i = 0; i < cdr.ParamC; i++)
AddIrqQueue(cdr.Cmd, 0x800);
switch (cdr.Cmd) {
- case CdlSync:
- case CdlNop:
- case CdlForward:
- case CdlBackward:
- case CdlReadT:
- case CdlTest:
- case CdlID:
- case CdlReadToc:
- case CdlGetmode:
- case CdlGetlocL:
- case CdlGetlocP:
- case CdlGetTD:
- break;
-
case CdlSetloc:
- StopReading();
for (i = 0; i < 3; i++)
set_loc[i] = btoi(cdr.Param[i]);
- // FIXME: clean up this SetSector/SetSectorPlay mess,
- // there should be single var tracking current sector pos
- if (cdr.Play)
- i = msf2sec(cdr.SetSectorPlay);
- else
- i = msf2sec(cdr.SetSector);
+ 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;
- break;
-
- case CdlPlay:
- // Vib Ribbon: try same track again
- StopCdda();
-
-#if 0
- 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);
- }
- }
- }
-#endif
- // Vib Ribbon - decoded buffer IRQ for CDDA reading
- // - fixes ribbon timing + music CD mode
- //TODO?
- //CDRDBUF_INT( PSXCLK / 44100 * 0x100 );
-
- cdr.Play = TRUE;
-
- cdr.StatP |= STATUS_SEEK;
- cdr.StatP &= ~STATUS_ROTATING;
+ cdr.SetlocPending = 1;
break;
case CdlReadN:
- StopReading();
- cdr.Reading = 1;
- cdr.FirstSector = 1;
- cdr.Readed = 0xff;
- break;
-
- case CdlStandby:
- StopCdda();
- StopReading();
- break;
-
- case CdlStop:
- // GameShark CD Player: Reset CDDA to track start
- 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();
- break;
-
+ case CdlReadS:
case CdlPause:
- /*
- GameShark CD Player: save time for resume
-
- Twisted Metal - World Tour: don't mix Setloc / CdlPlay cursors
- */
-
StopCdda();
StopReading();
break;
StopReading();
break;
- case CdlMute:
- cdr.Muted = TRUE;
- // Duke Nukem - Time to Kill
- // - do not directly set cd-xa volume
- //SPU_writeRegister( H_CDLeft, 0x0000 );
- //SPU_writeRegister( H_CDRight, 0x0000 );
- break;
-
- case CdlDemute:
- cdr.Muted = FALSE;
-
- // Duke Nukem - Time to Kill
- // - do not directly set cd-xa volume
- //SPU_writeRegister( H_CDLeft, 0x7f00 );
- //SPU_writeRegister( H_CDRight, 0x7f00 );
- break;
-
- case CdlSetfilter:
- cdr.File = cdr.Param[0];
- cdr.Channel = cdr.Param[1];
- break;
-
case CdlSetmode:
CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
StopCdda();
break;
-
- case CdlGetTN:
- //AddIrqQueue(cdr.Cmd, 0x800);
-
- // GameShark CDX CD Player: very long time
- AddIrqQueue(cdr.Cmd, 0x100000);
- break;
-
- case CdlSeekL:
- case CdlSeekP:
- // Tomb Raider 2 - reset cdda
- StopCdda();
- StopReading();
- break;
-
- case CdlReadS:
- StopReading();
- cdr.Reading = 2;
- cdr.FirstSector = 1;
- cdr.Readed = 0xff;
- break;
-
- default:
- cdr.ParamC = 0;
- CDR_LOG_I("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd);
- return;
}
}
setIrq();
return;
case 2:
- cdr.AttenuatorLeft[0] = rt;
+ cdr.AttenuatorLeftToLeftT = rt;
return;
case 3:
- cdr.AttenuatorRight[0] = rt;
+ cdr.AttenuatorRightToLeftT = rt;
return;
}
}
cdr.ParamC = 0;
return;
case 2:
- cdr.AttenuatorLeft[1] = rt;
+ cdr.AttenuatorLeftToRightT = rt;
return;
case 3:
- if (rt == 0x20)
- CDR_LOG( "CD-XA Volume: %X %X | %X %X\n",
- cdr.AttenuatorLeft[0], cdr.AttenuatorLeft[1],
- cdr.AttenuatorRight[0], cdr.AttenuatorRight[1] );
+ 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;
}
// - fix boot
if( cdsize == 0 )
{
- switch (cdr.Mode & 0x30) {
- case 0x00: cdsize = 2048; break;
- case MODE_SIZE_2328: cdsize = 2328; break;
+ 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;
}
}
cdr.CurTrack = 1;
cdr.File = 1;
cdr.Channel = 1;
+ cdr.Reg2 = 0x1f;
+ cdr.Stat = NoIntr;
+ cdr.DriveState = DRIVESTATE_STANDBY;
+ cdr.StatP = STATUS_ROTATING;
pTransfer = cdr.Transfer;
// BIOS player - default values
- cdr.AttenuatorLeft[0] = 0x80;
- cdr.AttenuatorLeft[1] = 0x00;
- cdr.AttenuatorRight[0] = 0x00;
- cdr.AttenuatorRight[1] = 0x80;
+ cdr.AttenuatorLeftToLeft = 0x80;
+ cdr.AttenuatorLeftToRight = 0x00;
+ cdr.AttenuatorRightToLeft = 0x00;
+ cdr.AttenuatorRightToRight = 0x80;
getCdInfo();
}
if (Mode == 0 && !Config.Cdda)
CDR_stop();
- cdr.freeze_ver = 0x63647201;
+ cdr.freeze_ver = 0x63647202;
gzfreeze(&cdr, sizeof(cdr));
if (Mode == 1) {
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);
SysPrintf("cdrom: fixing up old savestate\n");
cdr.Reg2 = 7;
}
+ // also did not save Attenuator..
+ if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
+ | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
+ {
+ cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
+ }
}
}
}
void LidInterrupt() {
- cdr.LidCheck = 0x20; // start checker
-
getCdInfo();
-
StopCdda();
- CDRLID_INT( cdReadTime * 3 );
-
- // generate interrupt if none active - open or close
- if (cdr.Irq == 0 || cdr.Irq == 0xff) {
- cdr.Ctrl |= 0x80;
- cdr.Stat = NoIntr;
- AddIrqQueue(CdlNop, 0x800);
- }
+ cdrLidSeekInterrupt();
}