in cores/pce/mednafen/cdrom/scsicd.cpp [2794:3069]
uint32_t SCSICD_Run(scsicd_timestamp_t system_timestamp)
{
int32_t run_time = system_timestamp - lastts;
if(system_timestamp < lastts)
{
fprintf(stderr, "Meow: %d %d\n", system_timestamp, lastts);
assert(system_timestamp >= lastts);
}
monotonic_timestamp += run_time;
lastts = system_timestamp;
RunCDRead(system_timestamp, run_time);
RunCDDA(system_timestamp, run_time);
bool ResetNeeded = false;
if(RST_signal && !cd.last_RST_signal)
ResetNeeded = true;
cd.last_RST_signal = RST_signal;
if(ResetNeeded)
{
//puts("RST");
VirtualReset();
}
else if(CurrentPhase == PHASE_BUS_FREE)
{
if(SEL_signal)
{
if(WhichSystem == SCSICD_PCFX)
{
//if(cd_bus.DB == 0x84)
{
ChangePhase(PHASE_COMMAND);
}
}
else // PCE
{
ChangePhase(PHASE_COMMAND);
}
}
}
else if(ATN_signal && !REQ_signal && !ACK_signal)
{
//printf("Yay: %d %d\n", REQ_signal, ACK_signal);
ChangePhase(PHASE_MESSAGE_OUT);
}
else switch(CurrentPhase)
{
case PHASE_COMMAND:
if(REQ_signal && ACK_signal) // Data bus is valid nowww
{
//printf("Command Phase Byte I->T: %02x, %d\n", cd_bus.DB, cd.command_buffer_pos);
cd.command_buffer[cd.command_buffer_pos++] = cd_bus.DB;
SetREQ(FALSE);
}
if(!REQ_signal && !ACK_signal && cd.command_buffer_pos) // Received at least one byte, what should we do?
{
if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
{
const SCSICH *cmd_info_ptr;
if(WhichSystem == SCSICD_PCFX)
cmd_info_ptr = PCFXCommandDefs;
else
cmd_info_ptr = PCECommandDefs;
while(cmd_info_ptr->pretty_name && cmd_info_ptr->cmd != cd.command_buffer[0])
cmd_info_ptr++;
if(SCSILog)
{
char log_buffer[1024];
int lb_pos;
log_buffer[0] = 0;
lb_pos = snprintf(log_buffer, 1024, "Command: %02x, %s%s ", cd.command_buffer[0], cmd_info_ptr->pretty_name ? cmd_info_ptr->pretty_name : "!!BAD COMMAND!!",
(cmd_info_ptr->flags & SCF_UNTESTED) ? "(UNTESTED)" : "");
for(int i = 0; i < RequiredCDBLen[cd.command_buffer[0] >> 4]; i++)
lb_pos += snprintf(log_buffer + lb_pos, 1024 - lb_pos, "%02x ", cd.command_buffer[i]);
SCSILog("SCSI", "%s", log_buffer);
//puts(log_buffer);
}
if(cmd_info_ptr->pretty_name == NULL) // Command not found!
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_COMMAND);
//SCSIDBG("Bad Command: %02x\n", cd.command_buffer[0]);
if(SCSILog)
SCSILog("SCSI", "Bad Command: %02x", cd.command_buffer[0]);
cd.command_buffer_pos = 0;
}
else
{
if(cmd_info_ptr->flags & SCF_UNTESTED)
{
//SCSIDBG("Untested SCSI command: %02x, %s", cd.command_buffer[0], cmd_info_ptr->pretty_name);
}
if(TrayOpen && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
{
CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
}
else if(!Cur_CDIF && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
{
CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC);
}
else if(cd.DiscChanged && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
{
CommandCCError(SENSEKEY_UNIT_ATTENTION, NSE_DISC_CHANGED);
cd.DiscChanged = false;
}
else
{
bool prev_ps = (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING);
cmd_info_ptr->func(cd.command_buffer);
bool new_ps = (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING);
// A bit kludgey, but ehhhh.
if(!prev_ps && new_ps)
{
memset(cdda.sr, 0, sizeof(cdda.sr));
memset(cdda.OversampleBuffer, 0, sizeof(cdda.OversampleBuffer));
memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState));
//printf("CLEAR BUFFERS LALALA\n");
}
}
cd.command_buffer_pos = 0;
}
} // end if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
else // Otherwise, get more data for the command!
SetREQ(TRUE);
}
break;
case PHASE_DATA_OUT:
if(REQ_signal && ACK_signal) // Data bus is valid nowww
{
//printf("DATAOUT-SCSIIN: %d %02x\n", cd.data_out_pos, cd_bus.DB);
cd.data_out[cd.data_out_pos++] = cd_bus.DB;
SetREQ(FALSE);
}
else if(!REQ_signal && !ACK_signal && cd.data_out_pos)
{
if(cd.data_out_pos == cd.data_out_want)
{
cd.data_out_pos = 0;
if(cd.command_buffer[0] == 0x15)
FinishMODESELECT6(cd.data_out, cd.data_out_want);
else // Error out here? It shouldn't be reached:
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
else
SetREQ(TRUE);
}
break;
case PHASE_MESSAGE_OUT:
//printf("%d %d, %02x\n", REQ_signal, ACK_signal, cd_bus.DB);
if(REQ_signal && ACK_signal)
{
SetREQ(FALSE);
// ABORT message is 0x06, but the code isn't set up to be able to recover from a MESSAGE OUT phase back to the previous phase, so we treat any message as an ABORT.
// Real tests are needed on the PC-FX to determine its behavior.
// (Previously, ATN emulation was a bit broken, which resulted in the wrong data on the data bus in this code path in at least "Battle Heat", but it's fixed now and 0x06 is on the data bus).
//if(cd_bus.DB == 0x6) // ABORT message!
if(1)
{
//printf("[SCSICD] Abort Received(DB=0x%02x)\n", cd_bus.DB);
din->Flush();
cd.data_out_pos = cd.data_out_want = 0;
CDReadTimer = 0;
cdda.CDDAStatus = CDDASTATUS_STOPPED;
ChangePhase(PHASE_BUS_FREE);
}
//else
// printf("[SCSICD] Message to target: 0x%02x\n", cd_bus.DB);
}
break;
case PHASE_STATUS:
if(REQ_signal && ACK_signal)
{
SetREQ(FALSE);
cd.status_sent = TRUE;
}
if(!REQ_signal && !ACK_signal && cd.status_sent)
{
// Status sent, so get ready to send the message!
cd.status_sent = FALSE;
cd_bus.DB = cd.message_pending;
ChangePhase(PHASE_MESSAGE_IN);
}
break;
case PHASE_DATA_IN:
if(!REQ_signal && !ACK_signal)
{
//puts("REQ and ACK false");
if(din->in_count == 0) // aaand we're done!
{
CDIRQCallback(0x8000 | SCSICD_IRQ_DATA_TRANSFER_READY);
if(cd.data_transfer_done)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
cd.data_transfer_done = FALSE;
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
}
}
else
{
cd_bus.DB = din->ReadByte();
SetREQ(TRUE);
}
}
if(REQ_signal && ACK_signal)
{
//puts("REQ and ACK true");
SetREQ(FALSE);
}
break;
case PHASE_MESSAGE_IN:
if(REQ_signal && ACK_signal)
{
SetREQ(FALSE);
cd.message_sent = TRUE;
}
if(!REQ_signal && !ACK_signal && cd.message_sent)
{
cd.message_sent = FALSE;
ChangePhase(PHASE_BUS_FREE);
}
break;
}
int32_t next_time = 0x7fffffff;
if(CDReadTimer > 0 && CDReadTimer < next_time)
next_time = CDReadTimer;
if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING)
{
int32_t cdda_div_sexytime = (cdda.CDDADiv + (cdda.CDDADivAcc * (cdda.OversamplePos & 1)) + ((1 << 20) - 1)) >> 20;
if(cdda_div_sexytime > 0 && cdda_div_sexytime < next_time)
next_time = cdda_div_sexytime;
}
assert(next_time >= 0);
return(next_time);
}