mirror of
https://github.com/holub/mame
synced 2025-05-07 06:44:51 +03:00
Moved scsi protocol code from scsibus_device to scsihle_device, leaving scsibus_device to calculate the current bus contents and distribute it to each of the scsidev_device. [smf]
This commit is contained in:
parent
fd98bf8bde
commit
95dcf93a69
@ -1,637 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
SCSIBus.c
|
|
||||||
|
|
||||||
Implementation of a raw SCSI/SASI bus for machines that don't use a SCSI
|
scsibus.c
|
||||||
controler chip such as the RM Nimbus, which implements it as a bunch of
|
|
||||||
74LS series chips.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
#include "machine/scsihle.h"
|
|
||||||
#include "machine/scsibus.h"
|
#include "machine/scsibus.h"
|
||||||
#include "debugger.h"
|
|
||||||
#include "debug/debugcpu.h"
|
|
||||||
#include "debug/debugcon.h"
|
|
||||||
|
|
||||||
/*
|
void scsibus_device::scsi_update()
|
||||||
LOGLEVEL
|
|
||||||
0 no logging,
|
|
||||||
1 just commands
|
|
||||||
2 1 + data
|
|
||||||
3 2 + line changes
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOGLEVEL 0
|
|
||||||
|
|
||||||
#define LOG(level,...) if(LOGLEVEL>=level) logerror(__VA_ARGS__)
|
|
||||||
|
|
||||||
static const char *const linenames[] =
|
|
||||||
{
|
{
|
||||||
"select", "busy", "request", "acknoledge", "C/D", "I/O", "message", "reset"
|
UINT32 newdata = SCSI_MASK_ALL;
|
||||||
};
|
|
||||||
|
|
||||||
static const char *const phasenames[] =
|
for( int i = 0; i < deviceCount; i++ )
|
||||||
{
|
{
|
||||||
"data out", "data in", "command", "status", "none", "none", "message out", "message in", "bus free","select"
|
newdata &= devices[ i ]->data_out;
|
||||||
};
|
|
||||||
|
|
||||||
void scsibus_device::dump_bytes(UINT8 *buff, int count)
|
|
||||||
{
|
|
||||||
int byteno;
|
|
||||||
|
|
||||||
for(byteno=0; byteno<count; byteno++)
|
|
||||||
{
|
|
||||||
logerror("%02X ",buff[byteno]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scsibus_device::dump_command_bytes()
|
UINT32 mask = data ^ newdata;
|
||||||
|
|
||||||
|
if( mask != 0 )
|
||||||
{
|
{
|
||||||
logerror("sending command 0x%02X to ScsiID %d\n",command[0],last_id);
|
data = newdata;
|
||||||
dump_bytes(command,cmd_idx);
|
|
||||||
logerror("\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::dump_data_bytes(int count)
|
for( int i = 0; i < deviceCount; i++ )
|
||||||
{
|
{
|
||||||
logerror("Data buffer[0..%d]\n",count);
|
devices[ i ]->scsi_in( data, mask );
|
||||||
dump_bytes(buffer,count);
|
|
||||||
logerror("\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::scsibus_read_data()
|
|
||||||
{
|
|
||||||
data_last = (bytes_left >= sectorbytes) ? sectorbytes : bytes_left;
|
|
||||||
|
|
||||||
LOG(2,"SCSIBUS:scsibus_read_data bytes_left=%04X, data_last=%04X\n",bytes_left,data_last);
|
|
||||||
|
|
||||||
if (data_last > 0)
|
|
||||||
{
|
|
||||||
devices[last_id]->ReadData(buffer, data_last);
|
|
||||||
bytes_left-=data_last;
|
|
||||||
}
|
|
||||||
|
|
||||||
data_idx=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::scsibus_write_data()
|
|
||||||
{
|
|
||||||
if (data_last > 0)
|
|
||||||
{
|
|
||||||
devices[last_id]->WriteData(buffer, data_last);
|
|
||||||
bytes_left-=data_last;
|
|
||||||
}
|
|
||||||
|
|
||||||
data_idx=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SCSI Bus read/write */
|
|
||||||
|
|
||||||
UINT8 scsibus_device::scsi_data_r()
|
|
||||||
{
|
|
||||||
UINT8 result = 0;
|
|
||||||
|
|
||||||
switch (phase)
|
|
||||||
{
|
|
||||||
case SCSI_PHASE_DATAIN:
|
|
||||||
result=buffer[data_idx];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_STATUS:
|
|
||||||
result=SCSI_STATUS_OK; // return command status
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_MESSAGE_IN:
|
|
||||||
result=0; // no errors for the time being !
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(2,"scsi_data_r : %02x phase=%s, data_idx=%d, cmd_idx=%d\n",result,phasenames[phase],data_idx,cmd_idx);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::scsi_data_w( UINT8 data )
|
|
||||||
{
|
|
||||||
switch (phase)
|
|
||||||
{
|
|
||||||
// Note this assumes we only have one initiator and therefore
|
|
||||||
// only one line active.
|
|
||||||
case SCSI_PHASE_BUS_FREE:
|
|
||||||
last_id=scsibus_driveno(data);
|
|
||||||
sectorbytes = devices[last_id]->GetSectorBytes();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_COMMAND:
|
|
||||||
command[cmd_idx]=data;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_DATAOUT:
|
|
||||||
|
|
||||||
//LOG(1,"SCSIBUS:bytes_left=%02X data_idx=%02X\n",bytes_left,data_idx);
|
|
||||||
|
|
||||||
if(IS_COMMAND(SCSI_CMD_FORMAT_UNIT))
|
|
||||||
{
|
|
||||||
// Only store the first 4 bytes of the bad block list (the header)
|
|
||||||
//if(data_idx<4)
|
|
||||||
buffer[data_idx]=data;
|
|
||||||
dump_data_bytes(4);
|
|
||||||
//else
|
|
||||||
// data_idx++;
|
|
||||||
|
|
||||||
// If we have the first byte, then cancel the dataout timout
|
|
||||||
if(data_idx==0)
|
|
||||||
dataout_timer->adjust(attotime::never);
|
|
||||||
|
|
||||||
// When we have the first 3 bytes, calculate how many more are in the
|
|
||||||
// bad block list.
|
|
||||||
if(data_idx==2)
|
|
||||||
{
|
|
||||||
bytes_left+=((buffer[2]<<8)+buffer[3]);
|
|
||||||
LOG(1,"format_unit reading an extra %d bytes\n",bytes_left-4);
|
|
||||||
dump_data_bytes(4);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
buffer[data_idx]=data;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get/Set lines */
|
|
||||||
|
|
||||||
UINT8 scsibus_device::get_scsi_line(UINT8 lineno)
|
|
||||||
{
|
|
||||||
UINT8 result=0;
|
|
||||||
|
|
||||||
switch (lineno)
|
|
||||||
{
|
|
||||||
case SCSI_LINE_BSY: result=(linestate & (1<<SCSI_LINE_BSY)) >> SCSI_LINE_BSY; break;
|
|
||||||
case SCSI_LINE_SEL: result=(linestate & (1<<SCSI_LINE_SEL)) >> SCSI_LINE_SEL; break;
|
|
||||||
case SCSI_LINE_CD: result=(linestate & (1<<SCSI_LINE_CD )) >> SCSI_LINE_CD; break;
|
|
||||||
case SCSI_LINE_IO: result=(linestate & (1<<SCSI_LINE_IO )) >> SCSI_LINE_IO; break;
|
|
||||||
case SCSI_LINE_MSG: result=(linestate & (1<<SCSI_LINE_MSG)) >> SCSI_LINE_MSG; break;
|
|
||||||
case SCSI_LINE_REQ: result=(linestate & (1<<SCSI_LINE_REQ)) >> SCSI_LINE_REQ; break;
|
|
||||||
case SCSI_LINE_ACK: result=(linestate & (1<<SCSI_LINE_ACK)) >> SCSI_LINE_ACK; break;
|
|
||||||
case SCSI_LINE_ATN: result=(linestate & (1<<SCSI_LINE_ATN)) >> SCSI_LINE_MSG; break;
|
|
||||||
case SCSI_LINE_RST: result=(linestate & (1<<SCSI_LINE_RST)) >> SCSI_LINE_RST; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(3,"get_scsi_line(%s)=%d\n",linenames[lineno],result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::set_scsi_line(UINT8 line, UINT8 state)
|
|
||||||
{
|
|
||||||
UINT8 changed;
|
|
||||||
|
|
||||||
changed = ((linestate & (1<<line)) != (state << line));
|
|
||||||
|
|
||||||
LOG(3,"set_scsi_line(%s,%d), changed=%d, linestate=%02X\n",linenames[line],state,changed,linestate);
|
|
||||||
|
|
||||||
if(changed)
|
|
||||||
{
|
|
||||||
if (line==SCSI_LINE_ACK)
|
|
||||||
set_scsi_line_ack(state);
|
|
||||||
else
|
|
||||||
set_scsi_line_now(line,state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::set_scsi_line_now(UINT8 line, UINT8 state)
|
|
||||||
{
|
|
||||||
if(state)
|
|
||||||
linestate |= (1<<line);
|
|
||||||
else
|
|
||||||
linestate &= ~(1<<line);
|
|
||||||
|
|
||||||
scsi_in_line_changed(line,state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::set_scsi_line_ack(UINT8 state)
|
|
||||||
{
|
|
||||||
ack_timer->adjust(attotime::from_nsec(ACK_DELAY_NS),state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::scsibus_exec_command()
|
|
||||||
{
|
|
||||||
int command_local = 0;
|
|
||||||
int newphase;
|
|
||||||
|
|
||||||
if(LOGLEVEL)
|
|
||||||
dump_command_bytes();
|
|
||||||
|
|
||||||
//is_linked=command[cmd_idx-1] & 0x01;
|
|
||||||
is_linked=0;
|
|
||||||
|
|
||||||
// Check for locally executed commands, and if found execute them
|
|
||||||
switch (command[0])
|
|
||||||
{
|
|
||||||
// Format unit
|
|
||||||
case SCSI_CMD_FORMAT_UNIT:
|
|
||||||
LOG(1,"SCSIBUS: format unit command[1]=%02X & 0x10\n",(command[1] & 0x10));
|
|
||||||
command_local=1;
|
|
||||||
if((command[1] & 0x10)==0x10)
|
|
||||||
devices[last_id]->SetPhase(SCSI_PHASE_DATAOUT);
|
|
||||||
else
|
|
||||||
devices[last_id]->SetPhase(SCSI_PHASE_STATUS);
|
|
||||||
|
|
||||||
bytes_left=4;
|
|
||||||
dataout_timer->adjust(attotime::from_seconds(FORMAT_UNIT_TIMEOUT));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_CMD_SEARCH_DATA_EQUAL:
|
|
||||||
LOG(1,"SCSIBUS: Search_data_equaln");
|
|
||||||
command_local=1;
|
|
||||||
bytes_left=0;
|
|
||||||
devices[last_id]->SetPhase(SCSI_PHASE_STATUS);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_CMD_READ_DEFECT:
|
|
||||||
LOG(1,"SCSIBUS: read defect list\n");
|
|
||||||
command_local=1;
|
|
||||||
|
|
||||||
buffer[0] = 0x00;
|
|
||||||
buffer[1] = command[2];
|
|
||||||
buffer[3] = 0x00; // defect list len msb
|
|
||||||
buffer[4] = 0x00; // defect list len lsb
|
|
||||||
|
|
||||||
bytes_left=4;
|
|
||||||
devices[last_id]->SetPhase(SCSI_PHASE_DATAIN);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// write buffer
|
|
||||||
case SCSI_CMD_BUFFER_WRITE:
|
|
||||||
LOG(1,"SCSIBUS: write_buffer\n");
|
|
||||||
command_local=1;
|
|
||||||
bytes_left=(command[7]<<8)+command[8];
|
|
||||||
devices[last_id]->SetPhase(SCSI_PHASE_DATAOUT);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// read buffer
|
|
||||||
case SCSI_CMD_BUFFER_READ:
|
|
||||||
LOG(1,"SCSIBUS: read_buffer\n");
|
|
||||||
command_local=1;
|
|
||||||
bytes_left=(command[7]<<8)+command[8];
|
|
||||||
devices[last_id]->SetPhase(SCSI_PHASE_DATAIN);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Check for locally executed command, if not then pass it on
|
|
||||||
// to the disk driver
|
|
||||||
if(!command_local)
|
|
||||||
{
|
|
||||||
devices[last_id]->SetCommand(command, cmd_idx);
|
|
||||||
devices[last_id]->ExecCommand(&bytes_left);
|
|
||||||
data_idx=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
devices[last_id]->GetPhase(&newphase);
|
|
||||||
|
|
||||||
scsi_change_phase(newphase);
|
|
||||||
|
|
||||||
LOG(1,"SCSIBUS:bytes_left=%02X data_idx=%02X\n",bytes_left,data_idx);
|
|
||||||
|
|
||||||
// This is correct as we need to read from disk for commands other than just read data
|
|
||||||
if ((phase == SCSI_PHASE_DATAIN) && (!command_local))
|
|
||||||
scsibus_read_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::check_process_dataout()
|
|
||||||
{
|
|
||||||
int capacity=0;
|
|
||||||
int tracks;
|
|
||||||
adaptec_sense_t *sense;
|
|
||||||
|
|
||||||
LOG(1,"SCSIBUS:check_process_dataout cmd=%02X\n",command[0]);
|
|
||||||
|
|
||||||
switch (command[0])
|
|
||||||
{
|
|
||||||
case SCSI_CMD_MODE_SELECT:
|
|
||||||
sense=(adaptec_sense_t *)buffer;
|
|
||||||
tracks=(sense->cylinder_count[0]<<8)+sense->cylinder_count[1];
|
|
||||||
capacity=(tracks * sense->head_count * 17);
|
|
||||||
LOG(1,"Tracks=%d, Heads=%d sec/track=%d\n",tracks,sense->head_count,sense->sectors_per_track);
|
|
||||||
LOG(1,"Setting disk capacity to %d blocks\n",capacity);
|
|
||||||
dump_data_bytes(0x16);
|
|
||||||
//debugger_break(device->machine());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void scsibus_device::scsi_in_line_changed(UINT8 line, UINT8 state)
|
|
||||||
{
|
|
||||||
void *hdfile;
|
|
||||||
|
|
||||||
// Reset aborts and returns to bus free
|
|
||||||
if((line==SCSI_LINE_RST) && (state==0))
|
|
||||||
{
|
|
||||||
scsi_change_phase(SCSI_PHASE_BUS_FREE);
|
|
||||||
cmd_idx=0;
|
|
||||||
data_idx=0;
|
|
||||||
is_linked=0;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (phase)
|
|
||||||
{
|
|
||||||
case SCSI_PHASE_BUS_FREE:
|
|
||||||
if((line==SCSI_LINE_SEL) && (devices[last_id]!=NULL))
|
|
||||||
{
|
|
||||||
// Check to see if device had image file mounted, if not, do not set busy,
|
|
||||||
// and stay busfree.
|
|
||||||
devices[last_id]->GetDevice(&hdfile);
|
|
||||||
if(hdfile!=(void *)NULL)
|
|
||||||
{
|
|
||||||
if(state==0)
|
|
||||||
sel_timer->adjust(attotime::from_nsec(BSY_DELAY_NS));
|
|
||||||
else
|
|
||||||
scsi_change_phase(SCSI_PHASE_COMMAND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_COMMAND:
|
|
||||||
if(line==SCSI_LINE_ACK)
|
|
||||||
{
|
|
||||||
if(state)
|
|
||||||
{
|
|
||||||
cmd_idx++;
|
|
||||||
|
|
||||||
// If the command is ready go and execute it
|
|
||||||
if(cmd_idx==get_scsi_cmd_len(command[0]))
|
|
||||||
{
|
|
||||||
scsibus_exec_command();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_DATAIN:
|
|
||||||
if(line==SCSI_LINE_ACK)
|
|
||||||
{
|
|
||||||
if(state)
|
|
||||||
{
|
|
||||||
data_idx++;
|
|
||||||
|
|
||||||
// check to see if we have reached the end of the block buffer
|
|
||||||
// and that there is more data to read from the scsi disk
|
|
||||||
if(data_idx==sectorbytes)
|
|
||||||
{
|
|
||||||
scsibus_read_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(data_idx == data_last && bytes_left == 0)
|
|
||||||
scsi_change_phase(SCSI_PHASE_STATUS);
|
|
||||||
else
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_DATAOUT:
|
|
||||||
if(line==SCSI_LINE_ACK)
|
|
||||||
{
|
|
||||||
if(state)
|
|
||||||
{
|
|
||||||
data_idx++;
|
|
||||||
|
|
||||||
// If the data buffer is full flush it to the SCSI disk
|
|
||||||
|
|
||||||
data_last = (bytes_left >= sectorbytes) ? sectorbytes : bytes_left;
|
|
||||||
|
|
||||||
if(data_idx == data_last)
|
|
||||||
scsibus_write_data();
|
|
||||||
|
|
||||||
if(data_idx == 0 && bytes_left == 0)
|
|
||||||
{
|
|
||||||
check_process_dataout();
|
|
||||||
scsi_change_phase(SCSI_PHASE_STATUS);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_STATUS:
|
|
||||||
if(line==SCSI_LINE_ACK)
|
|
||||||
{
|
|
||||||
if(state)
|
|
||||||
{
|
|
||||||
if(cmd_idx > 0)
|
|
||||||
{
|
|
||||||
scsi_change_phase(SCSI_PHASE_MESSAGE_IN);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cmd_idx++;
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_MESSAGE_IN:
|
|
||||||
if(line==SCSI_LINE_ACK)
|
|
||||||
{
|
|
||||||
if(state)
|
|
||||||
{
|
|
||||||
if(cmd_idx > 0)
|
|
||||||
{
|
|
||||||
if(is_linked)
|
|
||||||
scsi_change_phase(SCSI_PHASE_COMMAND);
|
|
||||||
else
|
|
||||||
scsi_change_phase(SCSI_PHASE_BUS_FREE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cmd_idx++;
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::scsi_out_line_change(UINT8 line, UINT8 state)
|
|
||||||
{
|
|
||||||
if(line==SCSI_LINE_REQ)
|
|
||||||
scsi_out_line_req(state);
|
|
||||||
else
|
|
||||||
scsi_out_line_change_now(line,state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::scsi_out_line_change_now(UINT8 line, UINT8 state)
|
|
||||||
{
|
|
||||||
if(state)
|
|
||||||
linestate |= (1<<line);
|
|
||||||
else
|
|
||||||
linestate &= ~(1<<line);
|
|
||||||
|
|
||||||
LOG(3,"scsi_out_line_change(%s,%d)\n",linenames[line],state);
|
|
||||||
|
|
||||||
if(m_scsicb != NULL)
|
|
||||||
{
|
|
||||||
switch (line)
|
|
||||||
{
|
|
||||||
case SCSI_LINE_BSY: m_scsicb->out_bsy_func(state); break;
|
|
||||||
case SCSI_LINE_SEL: m_scsicb->out_sel_func(state); break;
|
|
||||||
case SCSI_LINE_CD: m_scsicb->out_cd_func(state); break;
|
|
||||||
case SCSI_LINE_IO: m_scsicb->out_io_func(state); break;
|
|
||||||
case SCSI_LINE_MSG: m_scsicb->out_msg_func(state); break;
|
|
||||||
case SCSI_LINE_REQ: m_scsicb->out_req_func(state); break;
|
|
||||||
case SCSI_LINE_ACK: m_scsicb->out_ack_func(state); break;
|
|
||||||
case SCSI_LINE_ATN: m_scsicb->out_atn_func(state); break;
|
|
||||||
case SCSI_LINE_RST: m_scsicb->out_rst_func(state); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::scsi_out_line_req(UINT8 state)
|
|
||||||
{
|
|
||||||
req_timer->adjust(attotime::from_nsec(REQ_DELAY_NS),state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
|
|
||||||
{
|
|
||||||
switch( tid )
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
scsi_out_line_change_now(SCSI_LINE_REQ, param);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
set_scsi_line_now(SCSI_LINE_ACK, param);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
scsi_out_line_change_now(SCSI_LINE_BSY, param);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
// Some drives, notably the ST225N and ST125N, accept fromat unit commands
|
|
||||||
// with flags set indicating that bad block data should be transfered but
|
|
||||||
// don't then implemnt a data in phase, this timeout it to catch these !
|
|
||||||
if(IS_COMMAND(SCSI_CMD_FORMAT_UNIT) && (data_idx==0))
|
|
||||||
scsi_change_phase(SCSI_PHASE_STATUS);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsibus_device::scsi_change_phase(UINT8 newphase)
|
|
||||||
{
|
|
||||||
LOG(1,"scsi_change_phase() from=%s, to=%s\n",phasenames[phase],phasenames[newphase]);
|
|
||||||
|
|
||||||
phase=newphase;
|
|
||||||
cmd_idx=0;
|
|
||||||
data_idx=0;
|
|
||||||
|
|
||||||
switch(phase)
|
|
||||||
{
|
|
||||||
case SCSI_PHASE_BUS_FREE:
|
|
||||||
scsi_out_line_change(SCSI_LINE_CD,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_IO,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_MSG,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_BSY,1);
|
|
||||||
LOG(1,"SCSIBUS: done\n\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_COMMAND:
|
|
||||||
scsi_out_line_change(SCSI_LINE_CD,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_IO,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_MSG,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
LOG(1,"\nSCSIBUS: Command begin\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_DATAOUT:
|
|
||||||
scsi_out_line_change(SCSI_LINE_CD,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_IO,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_MSG,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_DATAIN:
|
|
||||||
scsi_out_line_change(SCSI_LINE_CD,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_IO,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_MSG,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_STATUS:
|
|
||||||
scsi_out_line_change(SCSI_LINE_CD,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_IO,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_MSG,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_MESSAGE_OUT:
|
|
||||||
scsi_out_line_change(SCSI_LINE_CD,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_IO,1);
|
|
||||||
scsi_out_line_change(SCSI_LINE_MSG,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCSI_PHASE_MESSAGE_IN:
|
|
||||||
scsi_out_line_change(SCSI_LINE_CD,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_IO,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_MSG,0);
|
|
||||||
scsi_out_line_change(SCSI_LINE_REQ,0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT8 scsibus_device::scsibus_driveno(UINT8 drivesel)
|
|
||||||
{
|
|
||||||
switch (drivesel)
|
|
||||||
{
|
|
||||||
case 0x01: return 0;
|
|
||||||
case 0x02: return 1;
|
|
||||||
case 0x04: return 2;
|
|
||||||
case 0x08: return 3;
|
|
||||||
case 0x10: return 4;
|
|
||||||
case 0x20: return 5;
|
|
||||||
case 0x40: return 6;
|
|
||||||
case 0x80: return 7;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the length of a SCSI command based on it's command byte type
|
|
||||||
int scsibus_device::get_scsi_cmd_len(int cbyte)
|
|
||||||
{
|
|
||||||
int group;
|
|
||||||
|
|
||||||
group = (cbyte>>5) & 7;
|
|
||||||
|
|
||||||
if (group == 0 || group == 3 || group == 6 || group == 7) return 6;
|
|
||||||
if (group == 1 || group == 2) return 10;
|
|
||||||
if (group == 5) return 12;
|
|
||||||
|
|
||||||
fatalerror("scsibus: Unknown SCSI command group %d, command byte=%02X\n", group,cbyte);
|
|
||||||
|
|
||||||
return 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scsibus_device::scsibus_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
scsibus_device::scsibus_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||||
@ -641,33 +36,18 @@ scsibus_device::scsibus_device(const machine_config &mconfig, const char *tag, d
|
|||||||
|
|
||||||
void scsibus_device::device_start()
|
void scsibus_device::device_start()
|
||||||
{
|
{
|
||||||
memset(devices, 0, sizeof(devices));
|
deviceCount = 0;
|
||||||
|
|
||||||
// All lines start high - inactive
|
|
||||||
linestate=0xFF;
|
|
||||||
|
|
||||||
// Start with bus free
|
|
||||||
phase=SCSI_PHASE_BUS_FREE;
|
|
||||||
|
|
||||||
// Setup req/ack/sel timers
|
|
||||||
req_timer=timer_alloc(0);
|
|
||||||
ack_timer=timer_alloc(1);
|
|
||||||
sel_timer=timer_alloc(2);
|
|
||||||
dataout_timer=timer_alloc(3);
|
|
||||||
|
|
||||||
for( device_t *device = first_subdevice(); device != NULL; device = device->next() )
|
for( device_t *device = first_subdevice(); device != NULL; device = device->next() )
|
||||||
{
|
{
|
||||||
scsihle_device *scsidev = dynamic_cast<scsihle_device *>(device);
|
scsidev_device *scsidev = dynamic_cast<scsidev_device *>(device);
|
||||||
if( scsidev != NULL )
|
if( scsidev != NULL )
|
||||||
{
|
{
|
||||||
devices[scsidev->GetDeviceID()] = scsidev;
|
devices[ deviceCount++ ] = scsidev;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
scsicb_device *scsicb = dynamic_cast<scsicb_device *>(device);
|
|
||||||
m_scsicb = scsicb;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data = SCSI_MASK_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
const device_type SCSIBUS = &device_creator<scsibus_device>;
|
const device_type SCSIBUS = &device_creator<scsibus_device>;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
SCSIBus.h
|
|
||||||
|
scsibus.h
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -8,77 +9,7 @@
|
|||||||
#ifndef _SCSIBUS_H_
|
#ifndef _SCSIBUS_H_
|
||||||
#define _SCSIBUS_H_
|
#define _SCSIBUS_H_
|
||||||
|
|
||||||
#include "machine/scsicb.h"
|
#include "machine/scsidev.h"
|
||||||
#include "machine/scsihle.h"
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
MACROS
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#define MCFG_SCSIBUS_ADD(_tag) \
|
|
||||||
MCFG_DEVICE_ADD(_tag, SCSIBUS, 0)
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
CONSTANTS
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#define SCSI_LINE_BSY 0
|
|
||||||
#define SCSI_LINE_SEL 1
|
|
||||||
#define SCSI_LINE_CD 2
|
|
||||||
#define SCSI_LINE_IO 3
|
|
||||||
#define SCSI_LINE_MSG 4
|
|
||||||
#define SCSI_LINE_REQ 5
|
|
||||||
#define SCSI_LINE_ACK 6
|
|
||||||
#define SCSI_LINE_ATN 7
|
|
||||||
#define SCSI_LINE_RST 8
|
|
||||||
|
|
||||||
#define REQ_DELAY_NS 90
|
|
||||||
#define ACK_DELAY_NS 90
|
|
||||||
#define BSY_DELAY_NS 50
|
|
||||||
|
|
||||||
#define CMD_BUF_SIZE 32
|
|
||||||
#define ADAPTEC_BUF_SIZE 1024
|
|
||||||
|
|
||||||
// scsidev
|
|
||||||
#define SCSI_CMD_BUFFER_WRITE ( 0x3b )
|
|
||||||
#define SCSI_CMD_BUFFER_READ ( 0x3c )
|
|
||||||
|
|
||||||
// scsihd
|
|
||||||
#define SCSI_CMD_FORMAT_UNIT 0x04
|
|
||||||
#define SCSI_CMD_SEARCH_DATA_EQUAL 0x31
|
|
||||||
#define SCSI_CMD_READ_DEFECT 0x37
|
|
||||||
|
|
||||||
|
|
||||||
#define IS_COMMAND(cmd) (command[0]==cmd)
|
|
||||||
#define IS_READ_COMMAND() ((command[0]==0x08) || (command[0]==0x28) || (command[0]==0xa8))
|
|
||||||
#define IS_WRITE_COMMAND() ((command[0]==0x0a) || (command[0]==0x2a))
|
|
||||||
|
|
||||||
#define FORMAT_UNIT_TIMEOUT 5
|
|
||||||
|
|
||||||
struct adaptec_sense_t
|
|
||||||
{
|
|
||||||
// parameter list
|
|
||||||
UINT8 reserved1[3];
|
|
||||||
UINT8 length;
|
|
||||||
|
|
||||||
// descriptor list
|
|
||||||
UINT8 density;
|
|
||||||
UINT8 reserved2[4];
|
|
||||||
UINT8 block_size[3];
|
|
||||||
|
|
||||||
// drive parameter list
|
|
||||||
UINT8 format_code;
|
|
||||||
UINT8 cylinder_count[2];
|
|
||||||
UINT8 head_count;
|
|
||||||
UINT8 reduced_write[2];
|
|
||||||
UINT8 write_precomp[2];
|
|
||||||
UINT8 landing_zone;
|
|
||||||
UINT8 step_pulse_code;
|
|
||||||
UINT8 bit_flags;
|
|
||||||
UINT8 sectors_per_track;
|
|
||||||
};
|
|
||||||
|
|
||||||
class scsibus_device : public device_t
|
class scsibus_device : public device_t
|
||||||
{
|
{
|
||||||
@ -87,60 +18,22 @@ public:
|
|||||||
scsibus_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
scsibus_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||||
/* SCSI Bus read/write */
|
/* SCSI Bus read/write */
|
||||||
|
|
||||||
UINT8 scsi_data_r();
|
void scsi_update();
|
||||||
void scsi_data_w( UINT8 data );
|
|
||||||
|
|
||||||
/* Get/Set lines */
|
|
||||||
|
|
||||||
UINT8 get_scsi_line(UINT8 lineno);
|
|
||||||
void set_scsi_line(UINT8 line, UINT8 state);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// device-level overrides
|
// device-level overrides
|
||||||
virtual void device_start();
|
virtual void device_start();
|
||||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int get_scsi_cmd_len(int cbyte);
|
scsidev_device *devices[16];
|
||||||
UINT8 scsibus_driveno(UINT8 drivesel);
|
|
||||||
void scsi_change_phase(UINT8 newphase);
|
|
||||||
void set_scsi_line_now(UINT8 line, UINT8 state);
|
|
||||||
void set_scsi_line_ack(UINT8 state);
|
|
||||||
void scsi_in_line_changed(UINT8 line, UINT8 state);
|
|
||||||
void scsi_out_line_change(UINT8 line, UINT8 state);
|
|
||||||
void scsi_out_line_change_now(UINT8 line, UINT8 state);
|
|
||||||
void scsi_out_line_req(UINT8 state);
|
|
||||||
void scsibus_read_data();
|
|
||||||
void scsibus_write_data();
|
|
||||||
void scsibus_exec_command();
|
|
||||||
void check_process_dataout();
|
|
||||||
void dump_command_bytes();
|
|
||||||
void dump_data_bytes(int count);
|
|
||||||
void dump_bytes(UINT8 *buff, int count);
|
|
||||||
|
|
||||||
scsihle_device *devices[8];
|
UINT32 data;
|
||||||
scsicb_device *m_scsicb;
|
int deviceCount;
|
||||||
|
|
||||||
UINT8 linestate;
|
|
||||||
UINT8 last_id;
|
|
||||||
UINT8 phase;
|
|
||||||
|
|
||||||
UINT8 command[CMD_BUF_SIZE];
|
|
||||||
UINT8 cmd_idx;
|
|
||||||
UINT8 is_linked;
|
|
||||||
|
|
||||||
UINT8 buffer[ADAPTEC_BUF_SIZE];
|
|
||||||
UINT16 data_idx;
|
|
||||||
int bytes_left;
|
|
||||||
int data_last;
|
|
||||||
int sectorbytes;
|
|
||||||
|
|
||||||
emu_timer *req_timer;
|
|
||||||
emu_timer *ack_timer;
|
|
||||||
emu_timer *sel_timer;
|
|
||||||
emu_timer *dataout_timer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MCFG_SCSIBUS_ADD(_tag) \
|
||||||
|
MCFG_DEVICE_ADD(_tag, SCSIBUS, 0)
|
||||||
|
|
||||||
// device type definition
|
// device type definition
|
||||||
extern const device_type SCSIBUS;
|
extern const device_type SCSIBUS;
|
||||||
|
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
scsicb.c
|
||||||
|
|
||||||
|
Implementation of a raw SCSI/SASI bus for machines that don't use a SCSI
|
||||||
|
controler chip such as the RM Nimbus, which implements it as a bunch of
|
||||||
|
74LS series chips.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#include "scsicb.h"
|
#include "scsicb.h"
|
||||||
#include "scsibus.h"
|
#include "scsibus.h"
|
||||||
|
|
||||||
scsicb_device::scsicb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
scsicb_device::scsicb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||||
: device_t(mconfig, SCSICB, "SCSI callback", tag, owner, clock)
|
: scsidev_device(mconfig, SCSICB, "SCSI callback", tag, owner, clock)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,6 +28,8 @@ void scsicb_device::device_config_complete()
|
|||||||
|
|
||||||
void scsicb_device::device_start()
|
void scsicb_device::device_start()
|
||||||
{
|
{
|
||||||
|
scsidev_device::device_start();
|
||||||
|
|
||||||
out_bsy_func.resolve(_out_bsy_func, *this);
|
out_bsy_func.resolve(_out_bsy_func, *this);
|
||||||
out_sel_func.resolve(_out_sel_func, *this);
|
out_sel_func.resolve(_out_sel_func, *this);
|
||||||
out_cd_func.resolve(_out_cd_func, *this);
|
out_cd_func.resolve(_out_cd_func, *this);
|
||||||
@ -29,28 +41,78 @@ void scsicb_device::device_start()
|
|||||||
out_rst_func.resolve(_out_rst_func, *this);
|
out_rst_func.resolve(_out_rst_func, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scsicb_device::scsi_in( UINT32 data, UINT32 mask )
|
||||||
|
{
|
||||||
|
linestate = data;
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_BSY ) != 0 )
|
||||||
|
{
|
||||||
|
out_bsy_func( (int) ( data & SCSI_MASK_BSY ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_SEL ) != 0 )
|
||||||
|
{
|
||||||
|
out_sel_func( (int) ( data & SCSI_MASK_SEL ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_CD ) != 0 )
|
||||||
|
{
|
||||||
|
out_cd_func( (int) ( data & SCSI_MASK_CD ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_IO ) != 0 )
|
||||||
|
{
|
||||||
|
out_io_func( (int) ( data & SCSI_MASK_IO ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_MSG ) != 0 )
|
||||||
|
{
|
||||||
|
out_msg_func( (int) ( data & SCSI_MASK_MSG ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_REQ ) != 0 )
|
||||||
|
{
|
||||||
|
out_req_func( (int) ( data & SCSI_MASK_REQ ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
out_ack_func( (int) ( data & SCSI_MASK_ACK ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_ATN ) != 0 )
|
||||||
|
{
|
||||||
|
out_ack_func( (int) ( data & SCSI_MASK_ATN ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( mask & SCSI_MASK_RST ) != 0 )
|
||||||
|
{
|
||||||
|
out_rst_func( (int) ( data & SCSI_MASK_RST ) != 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UINT8 scsicb_device::scsi_data_r()
|
UINT8 scsicb_device::scsi_data_r()
|
||||||
{
|
{
|
||||||
scsibus_device *m_scsibus = downcast<scsibus_device *>( owner() );
|
return linestate & SCSI_MASK_DATA;
|
||||||
return m_scsibus->scsi_data_r();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scsicb_device::scsi_data_w( UINT8 data )
|
void scsicb_device::scsi_data_w( UINT8 data )
|
||||||
{
|
{
|
||||||
scsibus_device *m_scsibus = downcast<scsibus_device *>( owner() );
|
scsi_out( data, SCSI_MASK_DATA );
|
||||||
m_scsibus->scsi_data_w( data );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT8 scsicb_device::get_scsi_line( UINT8 line )
|
UINT8 scsicb_device::get_scsi_line( UINT32 line )
|
||||||
{
|
{
|
||||||
scsibus_device *m_scsibus = downcast<scsibus_device *>( owner() );
|
UINT8 result = (int)( ( linestate & line ) != 0 );
|
||||||
return m_scsibus->get_scsi_line( line );
|
|
||||||
|
// LOG(3,"get_scsi_line(%s)=%d\n",linenames[lineno],result);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void scsicb_device::set_scsi_line( UINT8 line, UINT8 state )
|
void scsicb_device::set_scsi_line( UINT32 mask, UINT8 state )
|
||||||
{
|
{
|
||||||
scsibus_device *m_scsibus = downcast<scsibus_device *>( owner() );
|
scsi_out( state * mask, mask );
|
||||||
m_scsibus->set_scsi_line( line, state );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
READ8_MEMBER( scsicb_device::scsi_data_r )
|
READ8_MEMBER( scsicb_device::scsi_data_r )
|
||||||
@ -63,24 +125,24 @@ WRITE8_MEMBER( scsicb_device::scsi_data_w )
|
|||||||
scsi_data_w( data );
|
scsi_data_w( data );
|
||||||
}
|
}
|
||||||
|
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_bsy_r ) { return get_scsi_line(SCSI_LINE_BSY); }
|
READ_LINE_MEMBER( scsicb_device::scsi_bsy_r ) { return get_scsi_line(SCSI_MASK_BSY); }
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_sel_r ) { return get_scsi_line(SCSI_LINE_SEL); }
|
READ_LINE_MEMBER( scsicb_device::scsi_sel_r ) { return get_scsi_line(SCSI_MASK_SEL); }
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_cd_r ) { return get_scsi_line(SCSI_LINE_CD); }
|
READ_LINE_MEMBER( scsicb_device::scsi_cd_r ) { return get_scsi_line(SCSI_MASK_CD); }
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_io_r ) { return get_scsi_line(SCSI_LINE_IO); }
|
READ_LINE_MEMBER( scsicb_device::scsi_io_r ) { return get_scsi_line(SCSI_MASK_IO); }
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_msg_r ) { return get_scsi_line(SCSI_LINE_MSG); }
|
READ_LINE_MEMBER( scsicb_device::scsi_msg_r ) { return get_scsi_line(SCSI_MASK_MSG); }
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_req_r ) { return get_scsi_line(SCSI_LINE_REQ); }
|
READ_LINE_MEMBER( scsicb_device::scsi_req_r ) { return get_scsi_line(SCSI_MASK_REQ); }
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_ack_r ) { return get_scsi_line(SCSI_LINE_ACK); }
|
READ_LINE_MEMBER( scsicb_device::scsi_ack_r ) { return get_scsi_line(SCSI_MASK_ACK); }
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_atn_r ) { return get_scsi_line(SCSI_LINE_ATN); }
|
READ_LINE_MEMBER( scsicb_device::scsi_atn_r ) { return get_scsi_line(SCSI_MASK_ATN); }
|
||||||
READ_LINE_MEMBER( scsicb_device::scsi_rst_r ) { return get_scsi_line(SCSI_LINE_RST); }
|
READ_LINE_MEMBER( scsicb_device::scsi_rst_r ) { return get_scsi_line(SCSI_MASK_RST); }
|
||||||
|
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_bsy_w ) { set_scsi_line(SCSI_LINE_BSY, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_bsy_w ) { set_scsi_line(SCSI_MASK_BSY, state); }
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_sel_w ) { set_scsi_line(SCSI_LINE_SEL, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_sel_w ) { set_scsi_line(SCSI_MASK_SEL, state); }
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_cd_w ) { set_scsi_line(SCSI_LINE_CD, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_cd_w ) { set_scsi_line(SCSI_MASK_CD, state); }
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_io_w ) { set_scsi_line(SCSI_LINE_IO, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_io_w ) { set_scsi_line(SCSI_MASK_IO, state); }
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_msg_w ) { set_scsi_line(SCSI_LINE_MSG, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_msg_w ) { set_scsi_line(SCSI_MASK_MSG, state); }
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_req_w ) { set_scsi_line(SCSI_LINE_REQ, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_req_w ) { set_scsi_line(SCSI_MASK_REQ, state); }
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_ack_w ) { set_scsi_line(SCSI_LINE_ACK, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_ack_w ) { set_scsi_line(SCSI_MASK_ACK, state); }
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_atn_w ) { set_scsi_line(SCSI_LINE_ATN, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_atn_w ) { set_scsi_line(SCSI_MASK_ATN, state); }
|
||||||
WRITE_LINE_MEMBER( scsicb_device::scsi_rst_w ) { set_scsi_line(SCSI_LINE_RST, state); }
|
WRITE_LINE_MEMBER( scsicb_device::scsi_rst_w ) { set_scsi_line(SCSI_MASK_RST, state); }
|
||||||
|
|
||||||
const device_type SCSICB = &device_creator<scsicb_device>;
|
const device_type SCSICB = &device_creator<scsicb_device>;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
SCSICB.h
|
|
||||||
|
|
||||||
Callbacks from SCSI/SASI bus for machines that don't use a SCSI
|
scsicb.h
|
||||||
|
|
||||||
|
Implementation of a raw SCSI/SASI bus for machines that don't use a SCSI
|
||||||
controler chip such as the RM Nimbus, which implements it as a bunch of
|
controler chip such as the RM Nimbus, which implements it as a bunch of
|
||||||
74LS series chips.
|
74LS series chips.
|
||||||
|
|
||||||
@ -12,16 +13,7 @@
|
|||||||
#ifndef _SCSICB_H_
|
#ifndef _SCSICB_H_
|
||||||
#define _SCSICB_H_
|
#define _SCSICB_H_
|
||||||
|
|
||||||
#include "emu.h"
|
#include "scsidev.h"
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
MACROS
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#define MCFG_SCSICB_ADD(_tag, _intf) \
|
|
||||||
MCFG_DEVICE_ADD(_tag, SCSICB, 0) \
|
|
||||||
MCFG_DEVICE_CONFIG(_intf)
|
|
||||||
|
|
||||||
|
|
||||||
struct SCSICB_interface
|
struct SCSICB_interface
|
||||||
{
|
{
|
||||||
@ -36,29 +28,18 @@ struct SCSICB_interface
|
|||||||
devcb_write_line _out_rst_func;
|
devcb_write_line _out_rst_func;
|
||||||
};
|
};
|
||||||
|
|
||||||
class scsicb_device : public device_t,
|
class scsicb_device : public scsidev_device,
|
||||||
public SCSICB_interface
|
public SCSICB_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// construction/destruction
|
// construction/destruction
|
||||||
scsicb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
scsicb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||||
|
|
||||||
devcb_resolved_write_line out_bsy_func;
|
virtual void scsi_in( UINT32 data, UINT32 mask );
|
||||||
devcb_resolved_write_line out_sel_func;
|
|
||||||
devcb_resolved_write_line out_cd_func;
|
|
||||||
devcb_resolved_write_line out_io_func;
|
|
||||||
devcb_resolved_write_line out_msg_func;
|
|
||||||
devcb_resolved_write_line out_req_func;
|
|
||||||
devcb_resolved_write_line out_ack_func;
|
|
||||||
devcb_resolved_write_line out_atn_func;
|
|
||||||
devcb_resolved_write_line out_rst_func;
|
|
||||||
|
|
||||||
UINT8 scsi_data_r();
|
UINT8 scsi_data_r();
|
||||||
void scsi_data_w( UINT8 data );
|
void scsi_data_w( UINT8 data );
|
||||||
|
|
||||||
UINT8 get_scsi_line(UINT8 lineno);
|
|
||||||
void set_scsi_line(UINT8 line, UINT8 state);
|
|
||||||
|
|
||||||
DECLARE_READ8_MEMBER( scsi_data_r );
|
DECLARE_READ8_MEMBER( scsi_data_r );
|
||||||
DECLARE_WRITE8_MEMBER( scsi_data_w );
|
DECLARE_WRITE8_MEMBER( scsi_data_w );
|
||||||
|
|
||||||
@ -86,8 +67,28 @@ protected:
|
|||||||
// device-level overrides
|
// device-level overrides
|
||||||
virtual void device_config_complete();
|
virtual void device_config_complete();
|
||||||
virtual void device_start();
|
virtual void device_start();
|
||||||
|
|
||||||
|
private:
|
||||||
|
devcb_resolved_write_line out_bsy_func;
|
||||||
|
devcb_resolved_write_line out_sel_func;
|
||||||
|
devcb_resolved_write_line out_cd_func;
|
||||||
|
devcb_resolved_write_line out_io_func;
|
||||||
|
devcb_resolved_write_line out_msg_func;
|
||||||
|
devcb_resolved_write_line out_req_func;
|
||||||
|
devcb_resolved_write_line out_ack_func;
|
||||||
|
devcb_resolved_write_line out_atn_func;
|
||||||
|
devcb_resolved_write_line out_rst_func;
|
||||||
|
|
||||||
|
UINT8 get_scsi_line(UINT32 mask);
|
||||||
|
void set_scsi_line(UINT32 mask, UINT8 state);
|
||||||
|
|
||||||
|
UINT32 linestate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MCFG_SCSICB_ADD(_tag, _intf) \
|
||||||
|
MCFG_DEVICE_ADD(_tag, SCSICB, 0) \
|
||||||
|
MCFG_DEVICE_CONFIG(_intf)
|
||||||
|
|
||||||
// device type definition
|
// device type definition
|
||||||
extern const device_type SCSICB;
|
extern const device_type SCSICB;
|
||||||
|
|
||||||
|
@ -1,12 +1,28 @@
|
|||||||
/***************************************************************************
|
/*
|
||||||
|
|
||||||
scsidev.c - Base class for SCSI devices.
|
scsidev.c
|
||||||
|
|
||||||
***************************************************************************/
|
Base class for SCSI devices.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "machine/scsibus.h"
|
||||||
#include "machine/scsidev.h"
|
#include "machine/scsidev.h"
|
||||||
|
|
||||||
scsidev_device::scsidev_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) :
|
scsidev_device::scsidev_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) :
|
||||||
device_t(mconfig, type, name, tag, owner, clock)
|
device_t(mconfig, type, name, tag, owner, clock)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scsidev_device::device_start()
|
||||||
|
{
|
||||||
|
data_out = SCSI_MASK_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsidev_device::scsi_out( UINT32 data, UINT32 mask )
|
||||||
|
{
|
||||||
|
data_out = ( data_out & ~mask ) | ( data & mask );
|
||||||
|
|
||||||
|
scsibus_device *m_scsibus = downcast<scsibus_device *>( owner() );
|
||||||
|
m_scsibus->scsi_update();
|
||||||
|
}
|
||||||
|
@ -1,20 +1,45 @@
|
|||||||
/***************************************************************************
|
/*
|
||||||
|
|
||||||
scsidev.h
|
scsidev.h
|
||||||
|
|
||||||
***************************************************************************/
|
Base class for SCSI devices.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _SCSIDEV_H_
|
#ifndef _SCSIDEV_H_
|
||||||
#define _SCSIDEV_H_
|
#define _SCSIDEV_H_
|
||||||
|
|
||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
|
|
||||||
|
#define SCSI_MASK_DATA ( 0x00000ff )
|
||||||
|
#define SCSI_MASK_DATAH ( 0x000ff00 )
|
||||||
|
#define SCSI_MASK_DATAP ( 0x0010000 )
|
||||||
|
#define SCSI_MASK_BSY ( 0x0020000 )
|
||||||
|
#define SCSI_MASK_SEL ( 0x0040000 )
|
||||||
|
#define SCSI_MASK_CD ( 0x0080000 )
|
||||||
|
#define SCSI_MASK_IO ( 0x0100000 )
|
||||||
|
#define SCSI_MASK_MSG ( 0x0200000 )
|
||||||
|
#define SCSI_MASK_REQ ( 0x0400000 )
|
||||||
|
#define SCSI_MASK_ACK ( 0x0800000 )
|
||||||
|
#define SCSI_MASK_ATN ( 0x1000000 )
|
||||||
|
#define SCSI_MASK_RST ( 0x2000000 )
|
||||||
|
#define SCSI_MASK_ALL ( 0x3ffffff )
|
||||||
|
|
||||||
// base handler
|
// base handler
|
||||||
class scsidev_device : public device_t
|
class scsidev_device : public device_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// construction/destruction
|
// construction/destruction
|
||||||
scsidev_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock);
|
scsidev_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock);
|
||||||
|
|
||||||
|
virtual void scsi_in( UINT32 data, UINT32 mask ) = 0;
|
||||||
|
void scsi_out( UINT32 data, UINT32 mask );
|
||||||
|
|
||||||
|
UINT32 data_out;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// device-level overrides
|
||||||
|
virtual void device_start();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
/***************************************************************************
|
/*
|
||||||
|
|
||||||
scsihle.c - Base class for HLE'd SCSI devices.
|
scsihle.c
|
||||||
|
|
||||||
***************************************************************************/
|
Base class for HLE'd SCSI devices.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#include "emu.h"
|
|
||||||
#include "machine/scsihle.h"
|
#include "machine/scsihle.h"
|
||||||
|
|
||||||
scsihle_device::scsihle_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) :
|
scsihle_device::scsihle_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) :
|
||||||
@ -14,9 +15,19 @@ scsihle_device::scsihle_device(const machine_config &mconfig, device_type type,
|
|||||||
|
|
||||||
void scsihle_device::device_start()
|
void scsihle_device::device_start()
|
||||||
{
|
{
|
||||||
|
scsidev_device::device_start();
|
||||||
|
|
||||||
|
//req_timer = timer_alloc(0);
|
||||||
|
//ack_timer = timer_alloc(1);
|
||||||
|
sel_timer = timer_alloc(2);
|
||||||
|
dataout_timer = timer_alloc(3);
|
||||||
|
|
||||||
save_item( NAME( command ) );
|
save_item( NAME( command ) );
|
||||||
save_item( NAME( commandLength ) );
|
save_item( NAME( commandLength ) );
|
||||||
save_item( NAME( phase ) );
|
save_item( NAME( phase ) );
|
||||||
|
|
||||||
|
// Start with bus free
|
||||||
|
phase = SCSI_PHASE_BUS_FREE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SCSI_SENSE_SIZE 4
|
#define SCSI_SENSE_SIZE 4
|
||||||
@ -128,11 +139,6 @@ int scsihle_device::GetDeviceID()
|
|||||||
return scsiID;
|
return scsiID;
|
||||||
}
|
}
|
||||||
|
|
||||||
int scsihle_device::GetSectorBytes()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scsihle_device::static_set_deviceid( device_t &device, int _scsiID )
|
void scsihle_device::static_set_deviceid( device_t &device, int _scsiID )
|
||||||
{
|
{
|
||||||
scsihle_device &scsidev = downcast<scsihle_device &>(device);
|
scsihle_device &scsidev = downcast<scsihle_device &>(device);
|
||||||
@ -153,3 +159,571 @@ int SCSILengthFromUINT16( UINT8 *length )
|
|||||||
{
|
{
|
||||||
return ( *(length) << 8 ) | *(length + 1 );
|
return ( *(length) << 8 ) | *(length + 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BSY_DELAY_NS 50
|
||||||
|
//#define REQ_DELAY_NS 90
|
||||||
|
//#define ACK_DELAY_NS 90
|
||||||
|
|
||||||
|
static const char *const phasenames[] =
|
||||||
|
{
|
||||||
|
"data out", "data in", "command", "status", "none", "none", "message out", "message in", "bus free","select"
|
||||||
|
};
|
||||||
|
|
||||||
|
// scsidev
|
||||||
|
#define SCSI_CMD_BUFFER_WRITE ( 0x3b )
|
||||||
|
#define SCSI_CMD_BUFFER_READ ( 0x3c )
|
||||||
|
|
||||||
|
// scsihd
|
||||||
|
#define SCSI_CMD_FORMAT_UNIT 0x04
|
||||||
|
#define SCSI_CMD_SEARCH_DATA_EQUAL 0x31
|
||||||
|
#define SCSI_CMD_READ_DEFECT 0x37
|
||||||
|
|
||||||
|
|
||||||
|
#define IS_COMMAND(cmd) (command[0]==cmd)
|
||||||
|
#define IS_READ_COMMAND() ((command[0]==0x08) || (command[0]==0x28) || (command[0]==0xa8))
|
||||||
|
#define IS_WRITE_COMMAND() ((command[0]==0x0a) || (command[0]==0x2a))
|
||||||
|
|
||||||
|
#define FORMAT_UNIT_TIMEOUT 5
|
||||||
|
|
||||||
|
struct adaptec_sense_t
|
||||||
|
{
|
||||||
|
// parameter list
|
||||||
|
UINT8 reserved1[3];
|
||||||
|
UINT8 length;
|
||||||
|
|
||||||
|
// descriptor list
|
||||||
|
UINT8 density;
|
||||||
|
UINT8 reserved2[4];
|
||||||
|
UINT8 block_size[3];
|
||||||
|
|
||||||
|
// drive parameter list
|
||||||
|
UINT8 format_code;
|
||||||
|
UINT8 cylinder_count[2];
|
||||||
|
UINT8 head_count;
|
||||||
|
UINT8 reduced_write[2];
|
||||||
|
UINT8 write_precomp[2];
|
||||||
|
UINT8 landing_zone;
|
||||||
|
UINT8 step_pulse_code;
|
||||||
|
UINT8 bit_flags;
|
||||||
|
UINT8 sectors_per_track;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
LOGLEVEL
|
||||||
|
0 no logging,
|
||||||
|
1 just commands
|
||||||
|
2 1 + data
|
||||||
|
3 2 + line changes
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOGLEVEL 0
|
||||||
|
|
||||||
|
#define LOG(level,...) if(LOGLEVEL>=level) logerror(__VA_ARGS__)
|
||||||
|
|
||||||
|
//static const char *const linenames[] =
|
||||||
|
//{
|
||||||
|
// "select", "busy", "request", "acknoledge", "C/D", "I/O", "message", "reset"
|
||||||
|
//};
|
||||||
|
|
||||||
|
//void scsibus_device::set_scsi_line(UINT8 line, UINT8 state)
|
||||||
|
//{
|
||||||
|
// UINT8 changed = linestate[line] != state;
|
||||||
|
//
|
||||||
|
// LOG(3,"set_scsi_line(%s,%d), changed=%d\n",linenames[line],state,changed);
|
||||||
|
//
|
||||||
|
// if(changed)
|
||||||
|
// {
|
||||||
|
// if (line==SCSI_LINE_ACK)
|
||||||
|
// set_scsi_line_ack(state);
|
||||||
|
// else
|
||||||
|
// set_scsi_line_now(line,state);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void scsibus_device::set_scsi_line_now( UINT8 line, UINT8 state )
|
||||||
|
//{
|
||||||
|
// if( linestate[ line ] != state )
|
||||||
|
// {
|
||||||
|
// linestate[ line ] = state;
|
||||||
|
//
|
||||||
|
// for( int i = 0; i < deviceCount; i++ )
|
||||||
|
// {
|
||||||
|
// devices[ i ]->scsi_in_line_changed( line, state );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void scsibus_device::set_scsi_line_ack(UINT8 state)
|
||||||
|
//{
|
||||||
|
// ack_timer->adjust(attotime::from_nsec(ACK_DELAY_NS),state);
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void scsibus_device::scsi_out_line_change(UINT8 line, UINT8 state)
|
||||||
|
//{
|
||||||
|
// if(line==SCSI_LINE_REQ)
|
||||||
|
// scsi_out_line_req(state);
|
||||||
|
// else
|
||||||
|
// scsi_out_line_change_now(line,state);
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void scsibus_device::scsi_out_line_change_now(UINT8 line, UINT8 state)
|
||||||
|
//{
|
||||||
|
// if( linestate[ line ] != state )
|
||||||
|
// {
|
||||||
|
// linestate[ line ] = state;
|
||||||
|
//
|
||||||
|
// LOG(3,"scsi_out_line_change(%s,%d)\n",linenames[line],state);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void scsibus_device::scsi_out_line_req(UINT8 state)
|
||||||
|
//{
|
||||||
|
// req_timer->adjust(attotime::from_nsec(REQ_DELAY_NS),state);
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|
||||||
|
void scsihle_device::dump_bytes(UINT8 *buff, int count)
|
||||||
|
{
|
||||||
|
int byteno;
|
||||||
|
|
||||||
|
for(byteno=0; byteno<count; byteno++)
|
||||||
|
{
|
||||||
|
logerror("%02X ",buff[byteno]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::dump_command_bytes()
|
||||||
|
{
|
||||||
|
logerror("sending command 0x%02X to ScsiID %d\n",command[0],scsiID);
|
||||||
|
dump_bytes(command,cmd_idx);
|
||||||
|
logerror("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::dump_data_bytes(int count)
|
||||||
|
{
|
||||||
|
logerror("Data buffer[0..%d]\n",count);
|
||||||
|
dump_bytes(buffer,count);
|
||||||
|
logerror("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::scsibus_read_data()
|
||||||
|
{
|
||||||
|
data_last = (bytes_left >= sectorbytes) ? sectorbytes : bytes_left;
|
||||||
|
|
||||||
|
LOG(2,"SCSIBUS:scsibus_read_data bytes_left=%04X, data_last=%04X\n",bytes_left,data_last);
|
||||||
|
|
||||||
|
data_idx=0;
|
||||||
|
|
||||||
|
if (data_last > 0)
|
||||||
|
{
|
||||||
|
ReadData(buffer, data_last);
|
||||||
|
bytes_left-=data_last;
|
||||||
|
|
||||||
|
scsi_out( buffer[ data_idx++ ], SCSI_MASK_DATA );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::scsibus_write_data()
|
||||||
|
{
|
||||||
|
if (data_last > 0)
|
||||||
|
{
|
||||||
|
WriteData(buffer, data_last);
|
||||||
|
bytes_left-=data_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_idx=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
|
||||||
|
{
|
||||||
|
switch( tid )
|
||||||
|
{
|
||||||
|
// case 0:
|
||||||
|
// scsi_out_line_change_now(SCSI_LINE_REQ, param);
|
||||||
|
// break;
|
||||||
|
//
|
||||||
|
// case 1:
|
||||||
|
// set_scsi_line_now(SCSI_LINE_ACK, param);
|
||||||
|
// break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
scsi_out(param * SCSI_MASK_BSY, SCSI_MASK_BSY);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
// Some drives, notably the ST225N and ST125N, accept fromat unit commands
|
||||||
|
// with flags set indicating that bad block data should be transfered but
|
||||||
|
// don't then implemnt a data in phase, this timeout it to catch these !
|
||||||
|
if(IS_COMMAND(SCSI_CMD_FORMAT_UNIT) && (data_idx==0))
|
||||||
|
scsi_change_phase(SCSI_PHASE_STATUS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::scsibus_exec_command()
|
||||||
|
{
|
||||||
|
int command_local = 0;
|
||||||
|
int newphase;
|
||||||
|
|
||||||
|
if(LOGLEVEL)
|
||||||
|
dump_command_bytes();
|
||||||
|
|
||||||
|
//is_linked=command[cmd_idx-1] & 0x01;
|
||||||
|
is_linked=0;
|
||||||
|
|
||||||
|
// Check for locally executed commands, and if found execute them
|
||||||
|
switch (command[0])
|
||||||
|
{
|
||||||
|
// Format unit
|
||||||
|
case SCSI_CMD_FORMAT_UNIT:
|
||||||
|
LOG(1,"SCSIBUS: format unit command[1]=%02X & 0x10\n",(command[1] & 0x10));
|
||||||
|
command_local=1;
|
||||||
|
if((command[1] & 0x10)==0x10)
|
||||||
|
SetPhase(SCSI_PHASE_DATAOUT);
|
||||||
|
else
|
||||||
|
SetPhase(SCSI_PHASE_STATUS);
|
||||||
|
|
||||||
|
bytes_left=4;
|
||||||
|
dataout_timer->adjust(attotime::from_seconds(FORMAT_UNIT_TIMEOUT));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_SEARCH_DATA_EQUAL:
|
||||||
|
LOG(1,"SCSIBUS: Search_data_equaln");
|
||||||
|
command_local=1;
|
||||||
|
bytes_left=0;
|
||||||
|
SetPhase(SCSI_PHASE_STATUS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_READ_DEFECT:
|
||||||
|
LOG(1,"SCSIBUS: read defect list\n");
|
||||||
|
command_local=1;
|
||||||
|
|
||||||
|
buffer[0] = 0x00;
|
||||||
|
buffer[1] = command[2];
|
||||||
|
buffer[3] = 0x00; // defect list len msb
|
||||||
|
buffer[4] = 0x00; // defect list len lsb
|
||||||
|
|
||||||
|
bytes_left=4;
|
||||||
|
SetPhase(SCSI_PHASE_DATAIN);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// write buffer
|
||||||
|
case SCSI_CMD_BUFFER_WRITE:
|
||||||
|
LOG(1,"SCSIBUS: write_buffer\n");
|
||||||
|
command_local=1;
|
||||||
|
bytes_left=(command[7]<<8)+command[8];
|
||||||
|
SetPhase(SCSI_PHASE_DATAOUT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// read buffer
|
||||||
|
case SCSI_CMD_BUFFER_READ:
|
||||||
|
LOG(1,"SCSIBUS: read_buffer\n");
|
||||||
|
command_local=1;
|
||||||
|
bytes_left=(command[7]<<8)+command[8];
|
||||||
|
SetPhase(SCSI_PHASE_DATAIN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check for locally executed command, if not then pass it on
|
||||||
|
// to the disk driver
|
||||||
|
if(!command_local)
|
||||||
|
{
|
||||||
|
SetCommand(command, cmd_idx);
|
||||||
|
ExecCommand(&bytes_left);
|
||||||
|
data_idx=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPhase(&newphase);
|
||||||
|
|
||||||
|
scsi_change_phase(newphase);
|
||||||
|
|
||||||
|
LOG(1,"SCSIBUS:bytes_left=%02X data_idx=%02X\n",bytes_left,data_idx);
|
||||||
|
|
||||||
|
// This is correct as we need to read from disk for commands other than just read data
|
||||||
|
if ((phase == SCSI_PHASE_DATAIN) && (!command_local))
|
||||||
|
scsibus_read_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 scsihle_device::scsibus_driveno(UINT8 drivesel)
|
||||||
|
{
|
||||||
|
switch (drivesel)
|
||||||
|
{
|
||||||
|
case 0x01: return 0;
|
||||||
|
case 0x02: return 1;
|
||||||
|
case 0x04: return 2;
|
||||||
|
case 0x08: return 3;
|
||||||
|
case 0x10: return 4;
|
||||||
|
case 0x20: return 5;
|
||||||
|
case 0x40: return 6;
|
||||||
|
case 0x80: return 7;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::scsi_change_phase(UINT8 newphase)
|
||||||
|
{
|
||||||
|
LOG(1,"scsi_change_phase() from=%s, to=%s\n",phasenames[phase],phasenames[newphase]);
|
||||||
|
|
||||||
|
phase=newphase;
|
||||||
|
cmd_idx=0;
|
||||||
|
data_idx=0;
|
||||||
|
|
||||||
|
switch(phase)
|
||||||
|
{
|
||||||
|
case SCSI_PHASE_BUS_FREE:
|
||||||
|
scsi_out( SCSI_MASK_ALL, SCSI_MASK_ALL );
|
||||||
|
LOG(1,"SCSIBUS: done\n\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_COMMAND:
|
||||||
|
scsi_out( SCSI_MASK_DATA | SCSI_MASK_IO | SCSI_MASK_MSG, SCSI_MASK_DATA | SCSI_MASK_CD | SCSI_MASK_IO | SCSI_MASK_MSG | SCSI_MASK_REQ );
|
||||||
|
LOG(1,"\nSCSIBUS: Command begin\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_DATAOUT:
|
||||||
|
scsi_out( SCSI_MASK_CD | SCSI_MASK_IO | SCSI_MASK_MSG, SCSI_MASK_CD | SCSI_MASK_IO | SCSI_MASK_MSG | SCSI_MASK_REQ );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_DATAIN:
|
||||||
|
scsi_out( SCSI_MASK_DATA | SCSI_MASK_CD | SCSI_MASK_MSG, SCSI_MASK_DATA | SCSI_MASK_CD | SCSI_MASK_IO | SCSI_MASK_MSG | SCSI_MASK_REQ );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_STATUS:
|
||||||
|
scsi_out( SCSI_STATUS_OK | SCSI_MASK_MSG, SCSI_MASK_DATA | SCSI_MASK_CD | SCSI_MASK_IO | SCSI_MASK_MSG | SCSI_MASK_REQ );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_MESSAGE_OUT:
|
||||||
|
scsi_out( SCSI_MASK_IO, SCSI_MASK_CD | SCSI_MASK_IO | SCSI_MASK_MSG | SCSI_MASK_REQ );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_MESSAGE_IN:
|
||||||
|
scsi_out( 0, SCSI_MASK_DATA | SCSI_MASK_CD | SCSI_MASK_IO | SCSI_MASK_MSG | SCSI_MASK_REQ );// no errors for the time being !
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::scsi_in( UINT32 data, UINT32 mask )
|
||||||
|
{
|
||||||
|
// Reset aborts and returns to bus free
|
||||||
|
if( ( mask & SCSI_MASK_RST ) != 0 && ( data & SCSI_MASK_RST ) == 0 )
|
||||||
|
{
|
||||||
|
scsi_change_phase(SCSI_PHASE_BUS_FREE);
|
||||||
|
cmd_idx=0;
|
||||||
|
data_idx=0;
|
||||||
|
is_linked=0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (phase)
|
||||||
|
{
|
||||||
|
case SCSI_PHASE_BUS_FREE:
|
||||||
|
// Note this assumes we only have one initiator and therefore
|
||||||
|
// only one line active.
|
||||||
|
if( ( mask & SCSI_MASK_SEL ) != 0 && scsibus_driveno(data & SCSI_MASK_DATA) == scsiID)
|
||||||
|
{
|
||||||
|
void *hdfile;
|
||||||
|
// Check to see if device had image file mounted, if not, do not set busy,
|
||||||
|
// and stay busfree.
|
||||||
|
GetDevice(&hdfile);
|
||||||
|
if(hdfile!=(void *)NULL)
|
||||||
|
{
|
||||||
|
if( ( data & SCSI_MASK_SEL ) != 0 )
|
||||||
|
{
|
||||||
|
sectorbytes = GetSectorBytes();
|
||||||
|
scsi_change_phase(SCSI_PHASE_COMMAND);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sel_timer->adjust(attotime::from_nsec(BSY_DELAY_NS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_COMMAND:
|
||||||
|
if( ( mask & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
if( ( data & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
command[ cmd_idx++ ] = data & SCSI_MASK_DATA;
|
||||||
|
|
||||||
|
// If the command is ready go and execute it
|
||||||
|
if(cmd_idx==get_scsi_cmd_len(command[0]))
|
||||||
|
{
|
||||||
|
scsibus_exec_command();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scsi_out( 0, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scsi_out( SCSI_MASK_REQ, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_DATAIN:
|
||||||
|
if( ( mask & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
if( ( data & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
// check to see if we have reached the end of the block buffer
|
||||||
|
// and that there is more data to read from the scsi disk
|
||||||
|
if(data_idx==sectorbytes)
|
||||||
|
{
|
||||||
|
scsibus_read_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data_idx == data_last && bytes_left == 0)
|
||||||
|
{
|
||||||
|
scsi_change_phase(SCSI_PHASE_STATUS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scsi_out( buffer[ data_idx++ ], SCSI_MASK_DATA | SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scsi_out( SCSI_MASK_REQ, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_DATAOUT:
|
||||||
|
if( ( mask & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
if( ( data & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
//LOG(1,"SCSIBUS:bytes_left=%02X data_idx=%02X\n",bytes_left,data_idx);
|
||||||
|
buffer[data_idx++]=data & SCSI_MASK_DATA;
|
||||||
|
|
||||||
|
if(IS_COMMAND(SCSI_CMD_FORMAT_UNIT))
|
||||||
|
{
|
||||||
|
// If we have the first byte, then cancel the dataout timout
|
||||||
|
if(data_idx==1)
|
||||||
|
dataout_timer->adjust(attotime::never);
|
||||||
|
|
||||||
|
// When we have the first 3 bytes, calculate how many more are in the
|
||||||
|
// bad block list.
|
||||||
|
if(data_idx==3)
|
||||||
|
{
|
||||||
|
bytes_left+=((buffer[2]<<8)+buffer[3]);
|
||||||
|
LOG(1,"format_unit reading an extra %d bytes\n",bytes_left-4);
|
||||||
|
dump_data_bytes(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the data buffer is full flush it to the SCSI disk
|
||||||
|
|
||||||
|
data_last = (bytes_left >= sectorbytes) ? sectorbytes : bytes_left;
|
||||||
|
|
||||||
|
if(data_idx == data_last)
|
||||||
|
scsibus_write_data();
|
||||||
|
|
||||||
|
if(data_idx == 0 && bytes_left == 0)
|
||||||
|
{
|
||||||
|
check_process_dataout();
|
||||||
|
scsi_change_phase(SCSI_PHASE_STATUS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scsi_out( 0, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scsi_out( SCSI_MASK_REQ, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_STATUS:
|
||||||
|
if( ( mask & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
if( ( data & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
if(cmd_idx > 0)
|
||||||
|
{
|
||||||
|
scsi_change_phase(SCSI_PHASE_MESSAGE_IN);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scsi_out( 0, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd_idx++;
|
||||||
|
scsi_out( SCSI_MASK_REQ, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_PHASE_MESSAGE_IN:
|
||||||
|
if( ( mask & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
if( ( data & SCSI_MASK_ACK ) != 0 )
|
||||||
|
{
|
||||||
|
if(cmd_idx > 0)
|
||||||
|
{
|
||||||
|
if(is_linked)
|
||||||
|
scsi_change_phase(SCSI_PHASE_COMMAND);
|
||||||
|
else
|
||||||
|
scsi_change_phase(SCSI_PHASE_BUS_FREE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scsi_out( 0, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd_idx++;
|
||||||
|
scsi_out( SCSI_MASK_REQ, SCSI_MASK_REQ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsihle_device::check_process_dataout()
|
||||||
|
{
|
||||||
|
int capacity=0;
|
||||||
|
int tracks;
|
||||||
|
adaptec_sense_t *sense;
|
||||||
|
|
||||||
|
LOG(1,"SCSIBUS:check_process_dataout cmd=%02X\n",command[0]);
|
||||||
|
|
||||||
|
switch (command[0])
|
||||||
|
{
|
||||||
|
case SCSI_CMD_MODE_SELECT:
|
||||||
|
sense=(adaptec_sense_t *)buffer;
|
||||||
|
tracks=(sense->cylinder_count[0]<<8)+sense->cylinder_count[1];
|
||||||
|
capacity=(tracks * sense->head_count * 17);
|
||||||
|
LOG(1,"Tracks=%d, Heads=%d sec/track=%d\n",tracks,sense->head_count,sense->sectors_per_track);
|
||||||
|
LOG(1,"Setting disk capacity to %d blocks\n",capacity);
|
||||||
|
dump_data_bytes(0x16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the length of a SCSI command based on it's command byte type
|
||||||
|
int scsihle_device::get_scsi_cmd_len(int cbyte)
|
||||||
|
{
|
||||||
|
int group;
|
||||||
|
|
||||||
|
group = (cbyte>>5) & 7;
|
||||||
|
|
||||||
|
if (group == 0 || group == 3 || group == 6 || group == 7) return 6;
|
||||||
|
if (group == 1 || group == 2) return 10;
|
||||||
|
if (group == 5) return 12;
|
||||||
|
|
||||||
|
fatalerror("scsihle: Unknown SCSI command group %d, command byte=%02X\n", group,cbyte);
|
||||||
|
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
/***************************************************************************
|
/*
|
||||||
|
|
||||||
scsihle.h
|
scsihle.h
|
||||||
|
|
||||||
***************************************************************************/
|
Base class for HLE'd SCSI devices.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _SCSIHLE_H_
|
#ifndef _SCSIHLE_H_
|
||||||
#define _SCSIHLE_H_
|
#define _SCSIHLE_H_
|
||||||
|
|
||||||
|
#include "machine/scsibus.h"
|
||||||
#include "machine/scsidev.h"
|
#include "machine/scsidev.h"
|
||||||
|
|
||||||
// base handler
|
|
||||||
class scsihle_device : public scsidev_device
|
class scsihle_device : public scsidev_device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -26,7 +28,9 @@ public:
|
|||||||
virtual void SetPhase( int phase );
|
virtual void SetPhase( int phase );
|
||||||
virtual void GetPhase( int *phase );
|
virtual void GetPhase( int *phase );
|
||||||
virtual int GetDeviceID();
|
virtual int GetDeviceID();
|
||||||
virtual int GetSectorBytes();
|
virtual int GetSectorBytes() = 0;
|
||||||
|
|
||||||
|
virtual void scsi_in( UINT32 data, UINT32 mask );
|
||||||
|
|
||||||
// configuration helpers
|
// configuration helpers
|
||||||
static void static_set_deviceid(device_t &device, int _scsiID);
|
static void static_set_deviceid(device_t &device, int _scsiID);
|
||||||
@ -34,9 +38,35 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
// device-level overrides
|
// device-level overrides
|
||||||
virtual void device_start();
|
virtual void device_start();
|
||||||
|
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UINT8 command[16];
|
void scsi_change_phase(UINT8 newphase);
|
||||||
|
int get_scsi_cmd_len(int cbyte);
|
||||||
|
UINT8 scsibus_driveno(UINT8 drivesel);
|
||||||
|
void scsibus_read_data();
|
||||||
|
void scsibus_write_data();
|
||||||
|
void scsibus_exec_command();
|
||||||
|
void check_process_dataout();
|
||||||
|
void dump_command_bytes();
|
||||||
|
void dump_data_bytes(int count);
|
||||||
|
void dump_bytes(UINT8 *buff, int count);
|
||||||
|
|
||||||
|
//emu_timer *req_timer;
|
||||||
|
//emu_timer *ack_timer;
|
||||||
|
emu_timer *sel_timer;
|
||||||
|
emu_timer *dataout_timer;
|
||||||
|
|
||||||
|
UINT8 command[ 32 ];
|
||||||
|
UINT8 cmd_idx;
|
||||||
|
UINT8 is_linked;
|
||||||
|
|
||||||
|
UINT8 buffer[ 1024 ];
|
||||||
|
UINT16 data_idx;
|
||||||
|
int bytes_left;
|
||||||
|
int data_last;
|
||||||
|
int sectorbytes;
|
||||||
|
|
||||||
int commandLength;
|
int commandLength;
|
||||||
int phase;
|
int phase;
|
||||||
int scsiID;
|
int scsiID;
|
||||||
|
Loading…
Reference in New Issue
Block a user