mirror of
https://github.com/holub/mame
synced 2025-05-06 06:14:45 +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,639 +1,34 @@
|
||||
/*
|
||||
SCSIBus.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.
|
||||
scsibus.c
|
||||
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "machine/scsihle.h"
|
||||
#include "machine/scsibus.h"
|
||||
#include "debugger.h"
|
||||
#include "debug/debugcpu.h"
|
||||
#include "debug/debugcon.h"
|
||||
|
||||
/*
|
||||
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[] =
|
||||
void scsibus_device::scsi_update()
|
||||
{
|
||||
"select", "busy", "request", "acknoledge", "C/D", "I/O", "message", "reset"
|
||||
};
|
||||
UINT32 newdata = SCSI_MASK_ALL;
|
||||
|
||||
static const char *const phasenames[] =
|
||||
{
|
||||
"data out", "data in", "command", "status", "none", "none", "message out", "message in", "bus free","select"
|
||||
};
|
||||
|
||||
void scsibus_device::dump_bytes(UINT8 *buff, int count)
|
||||
{
|
||||
int byteno;
|
||||
|
||||
for(byteno=0; byteno<count; byteno++)
|
||||
for( int i = 0; i < deviceCount; i++ )
|
||||
{
|
||||
logerror("%02X ",buff[byteno]);
|
||||
}
|
||||
}
|
||||
|
||||
void scsibus_device::dump_command_bytes()
|
||||
{
|
||||
logerror("sending command 0x%02X to ScsiID %d\n",command[0],last_id);
|
||||
dump_bytes(command,cmd_idx);
|
||||
logerror("\n\n");
|
||||
}
|
||||
|
||||
void scsibus_device::dump_data_bytes(int count)
|
||||
{
|
||||
logerror("Data buffer[0..%d]\n",count);
|
||||
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;
|
||||
newdata &= devices[ i ]->data_out;
|
||||
}
|
||||
|
||||
data_idx=0;
|
||||
}
|
||||
UINT32 mask = data ^ newdata;
|
||||
|
||||
void scsibus_device::scsibus_write_data()
|
||||
{
|
||||
if (data_last > 0)
|
||||
if( mask != 0 )
|
||||
{
|
||||
devices[last_id]->WriteData(buffer, data_last);
|
||||
bytes_left-=data_last;
|
||||
}
|
||||
data = newdata;
|
||||
|
||||
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)
|
||||
for( int i = 0; i < deviceCount; i++ )
|
||||
{
|
||||
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;
|
||||
devices[ i ]->scsi_in( data, mask );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
: device_t(mconfig, SCSIBUS, "SCSI bus", tag, owner, clock)
|
||||
{
|
||||
@ -641,33 +36,18 @@ scsibus_device::scsibus_device(const machine_config &mconfig, const char *tag, d
|
||||
|
||||
void scsibus_device::device_start()
|
||||
{
|
||||
memset(devices, 0, sizeof(devices));
|
||||
|
||||
// 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);
|
||||
deviceCount = 0;
|
||||
|
||||
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 )
|
||||
{
|
||||
devices[scsidev->GetDeviceID()] = scsidev;
|
||||
}
|
||||
else
|
||||
{
|
||||
scsicb_device *scsicb = dynamic_cast<scsicb_device *>(device);
|
||||
m_scsicb = scsicb;
|
||||
devices[ deviceCount++ ] = scsidev;
|
||||
}
|
||||
}
|
||||
|
||||
data = SCSI_MASK_ALL;
|
||||
}
|
||||
|
||||
const device_type SCSIBUS = &device_creator<scsibus_device>;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
SCSIBus.h
|
||||
|
||||
scsibus.h
|
||||
|
||||
*/
|
||||
|
||||
@ -8,77 +9,7 @@
|
||||
#ifndef _SCSIBUS_H_
|
||||
#define _SCSIBUS_H_
|
||||
|
||||
#include "machine/scsicb.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;
|
||||
};
|
||||
#include "machine/scsidev.h"
|
||||
|
||||
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);
|
||||
/* SCSI Bus read/write */
|
||||
|
||||
UINT8 scsi_data_r();
|
||||
void scsi_data_w( UINT8 data );
|
||||
|
||||
/* Get/Set lines */
|
||||
|
||||
UINT8 get_scsi_line(UINT8 lineno);
|
||||
void set_scsi_line(UINT8 line, UINT8 state);
|
||||
void scsi_update();
|
||||
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start();
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
|
||||
|
||||
private:
|
||||
int get_scsi_cmd_len(int cbyte);
|
||||
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);
|
||||
scsidev_device *devices[16];
|
||||
|
||||
scsihle_device *devices[8];
|
||||
scsicb_device *m_scsicb;
|
||||
|
||||
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;
|
||||
UINT32 data;
|
||||
int deviceCount;
|
||||
};
|
||||
|
||||
#define MCFG_SCSIBUS_ADD(_tag) \
|
||||
MCFG_DEVICE_ADD(_tag, SCSIBUS, 0)
|
||||
|
||||
// device type definition
|
||||
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 "scsibus.h"
|
||||
|
||||
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()
|
||||
{
|
||||
scsidev_device::device_start();
|
||||
|
||||
out_bsy_func.resolve(_out_bsy_func, *this);
|
||||
out_sel_func.resolve(_out_sel_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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
scsibus_device *m_scsibus = downcast<scsibus_device *>( owner() );
|
||||
return m_scsibus->scsi_data_r();
|
||||
return linestate & SCSI_MASK_DATA;
|
||||
}
|
||||
|
||||
void scsicb_device::scsi_data_w( UINT8 data )
|
||||
{
|
||||
scsibus_device *m_scsibus = downcast<scsibus_device *>( owner() );
|
||||
m_scsibus->scsi_data_w( data );
|
||||
scsi_out( data, SCSI_MASK_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() );
|
||||
return m_scsibus->get_scsi_line( line );
|
||||
UINT8 result = (int)( ( linestate & line ) != 0 );
|
||||
|
||||
// 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() );
|
||||
m_scsibus->set_scsi_line( line, state );
|
||||
scsi_out( state * mask, mask );
|
||||
}
|
||||
|
||||
READ8_MEMBER( scsicb_device::scsi_data_r )
|
||||
@ -63,24 +125,24 @@ WRITE8_MEMBER( scsicb_device::scsi_data_w )
|
||||
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_sel_r ) { return get_scsi_line(SCSI_LINE_SEL); }
|
||||
READ_LINE_MEMBER( scsicb_device::scsi_cd_r ) { return get_scsi_line(SCSI_LINE_CD); }
|
||||
READ_LINE_MEMBER( scsicb_device::scsi_io_r ) { return get_scsi_line(SCSI_LINE_IO); }
|
||||
READ_LINE_MEMBER( scsicb_device::scsi_msg_r ) { return get_scsi_line(SCSI_LINE_MSG); }
|
||||
READ_LINE_MEMBER( scsicb_device::scsi_req_r ) { return get_scsi_line(SCSI_LINE_REQ); }
|
||||
READ_LINE_MEMBER( scsicb_device::scsi_ack_r ) { return get_scsi_line(SCSI_LINE_ACK); }
|
||||
READ_LINE_MEMBER( scsicb_device::scsi_atn_r ) { return get_scsi_line(SCSI_LINE_ATN); }
|
||||
READ_LINE_MEMBER( scsicb_device::scsi_rst_r ) { return get_scsi_line(SCSI_LINE_RST); }
|
||||
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_MASK_SEL); }
|
||||
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_MASK_IO); }
|
||||
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_MASK_REQ); }
|
||||
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_MASK_ATN); }
|
||||
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_sel_w ) { set_scsi_line(SCSI_LINE_SEL, state); }
|
||||
WRITE_LINE_MEMBER( scsicb_device::scsi_cd_w ) { set_scsi_line(SCSI_LINE_CD, state); }
|
||||
WRITE_LINE_MEMBER( scsicb_device::scsi_io_w ) { set_scsi_line(SCSI_LINE_IO, state); }
|
||||
WRITE_LINE_MEMBER( scsicb_device::scsi_msg_w ) { set_scsi_line(SCSI_LINE_MSG, state); }
|
||||
WRITE_LINE_MEMBER( scsicb_device::scsi_req_w ) { set_scsi_line(SCSI_LINE_REQ, state); }
|
||||
WRITE_LINE_MEMBER( scsicb_device::scsi_ack_w ) { set_scsi_line(SCSI_LINE_ACK, state); }
|
||||
WRITE_LINE_MEMBER( scsicb_device::scsi_atn_w ) { set_scsi_line(SCSI_LINE_ATN, state); }
|
||||
WRITE_LINE_MEMBER( scsicb_device::scsi_rst_w ) { set_scsi_line(SCSI_LINE_RST, 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_MASK_SEL, 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_MASK_IO, 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_MASK_REQ, 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_MASK_ATN, state); }
|
||||
WRITE_LINE_MEMBER( scsicb_device::scsi_rst_w ) { set_scsi_line(SCSI_MASK_RST, state); }
|
||||
|
||||
const device_type SCSICB = &device_creator<scsicb_device>;
|
||||
|
@ -1,9 +1,10 @@
|
||||
/*
|
||||
SCSICB.h
|
||||
|
||||
Callbacks from 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.
|
||||
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
|
||||
74LS series chips.
|
||||
|
||||
*/
|
||||
|
||||
@ -12,16 +13,7 @@
|
||||
#ifndef _SCSICB_H_
|
||||
#define _SCSICB_H_
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
/***************************************************************************
|
||||
MACROS
|
||||
***************************************************************************/
|
||||
|
||||
#define MCFG_SCSICB_ADD(_tag, _intf) \
|
||||
MCFG_DEVICE_ADD(_tag, SCSICB, 0) \
|
||||
MCFG_DEVICE_CONFIG(_intf)
|
||||
|
||||
#include "scsidev.h"
|
||||
|
||||
struct SCSICB_interface
|
||||
{
|
||||
@ -36,29 +28,18 @@ struct SCSICB_interface
|
||||
devcb_write_line _out_rst_func;
|
||||
};
|
||||
|
||||
class scsicb_device : public device_t,
|
||||
class scsicb_device : public scsidev_device,
|
||||
public SCSICB_interface
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
scsicb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
|
||||
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;
|
||||
virtual void scsi_in( UINT32 data, UINT32 mask );
|
||||
|
||||
UINT8 scsi_data_r();
|
||||
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_WRITE8_MEMBER( scsi_data_w );
|
||||
|
||||
@ -86,8 +67,28 @@ protected:
|
||||
// device-level overrides
|
||||
virtual void device_config_complete();
|
||||
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
|
||||
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"
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
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_
|
||||
#define _SCSIDEV_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
|
||||
class scsidev_device : public device_t
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
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
|
||||
|
@ -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"
|
||||
|
||||
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()
|
||||
{
|
||||
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( commandLength ) );
|
||||
save_item( NAME( phase ) );
|
||||
|
||||
// Start with bus free
|
||||
phase = SCSI_PHASE_BUS_FREE;
|
||||
}
|
||||
|
||||
#define SCSI_SENSE_SIZE 4
|
||||
@ -128,11 +139,6 @@ int scsihle_device::GetDeviceID()
|
||||
return scsiID;
|
||||
}
|
||||
|
||||
int scsihle_device::GetSectorBytes()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void scsihle_device::static_set_deviceid( device_t &device, int _scsiID )
|
||||
{
|
||||
scsihle_device &scsidev = downcast<scsihle_device &>(device);
|
||||
@ -153,3 +159,571 @@ int SCSILengthFromUINT16( UINT8 *length )
|
||||
{
|
||||
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_
|
||||
#define _SCSIHLE_H_
|
||||
|
||||
#include "machine/scsibus.h"
|
||||
#include "machine/scsidev.h"
|
||||
|
||||
// base handler
|
||||
class scsihle_device : public scsidev_device
|
||||
{
|
||||
public:
|
||||
@ -26,7 +28,9 @@ public:
|
||||
virtual void SetPhase( int phase );
|
||||
virtual void GetPhase( int *phase );
|
||||
virtual int GetDeviceID();
|
||||
virtual int GetSectorBytes();
|
||||
virtual int GetSectorBytes() = 0;
|
||||
|
||||
virtual void scsi_in( UINT32 data, UINT32 mask );
|
||||
|
||||
// configuration helpers
|
||||
static void static_set_deviceid(device_t &device, int _scsiID);
|
||||
@ -34,9 +38,35 @@ public:
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start();
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
|
||||
|
||||
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 phase;
|
||||
int scsiID;
|
||||
@ -64,34 +94,34 @@ extern int SCSILengthFromUINT16( UINT8 *length );
|
||||
// Status / Sense data taken from Adaptec ACB40x0 documentation.
|
||||
//
|
||||
|
||||
#define SCSI_STATUS_OK 0x00
|
||||
#define SCSI_STATUS_CHECK 0x02
|
||||
#define SCSI_STATUS_EQUAL 0x04
|
||||
#define SCSI_STATUS_BUSY 0x08
|
||||
#define SCSI_STATUS_OK 0x00
|
||||
#define SCSI_STATUS_CHECK 0x02
|
||||
#define SCSI_STATUS_EQUAL 0x04
|
||||
#define SCSI_STATUS_BUSY 0x08
|
||||
|
||||
#define SCSI_SENSE_ADDR_VALID 0x80
|
||||
#define SCSI_SENSE_NO_SENSE 0x00
|
||||
#define SCSI_SENSE_NO_INDEX 0x01
|
||||
#define SCSI_SENSE_SEEK_NOT_COMP 0x02
|
||||
#define SCSI_SENSE_WRITE_FAULT 0x03
|
||||
#define SCSI_SENSE_DRIVE_NOT_READY 0x04
|
||||
#define SCSI_SENSE_NO_TRACK0 0x06
|
||||
#define SCSI_SENSE_ID_CRC_ERROR 0x10
|
||||
#define SCSI_SENSE_UNCORRECTABLE 0x11
|
||||
#define SCSI_SENSE_ADDRESS_NF 0x12
|
||||
#define SCSI_SENSE_RECORD_NOT_FOUND 0x14
|
||||
#define SCSI_SENSE_SEEK_ERROR 0x15
|
||||
#define SCSI_SENSE_DATA_CHECK_RETRY 0x18
|
||||
#define SCSI_SENSE_ECC_VERIFY 0x19
|
||||
#define SCSI_SENSE_INTERLEAVE_ERROR 0x1A
|
||||
#define SCSI_SENSE_UNFORMATTED 0x1C
|
||||
#define SCSI_SENSE_ILLEGAL_COMMAND 0x20
|
||||
#define SCSI_SENSE_ILLEGAL_ADDRESS 0x21
|
||||
#define SCSI_SENSE_VOLUME_OVERFLOW 0x23
|
||||
#define SCSI_SENSE_BAD_ARGUMENT 0x24
|
||||
#define SCSI_SENSE_INVALID_LUN 0x25
|
||||
#define SCSI_SENSE_CART_CHANGED 0x28
|
||||
#define SCSI_SENSE_ERROR_OVERFLOW 0x2C
|
||||
#define SCSI_SENSE_ADDR_VALID 0x80
|
||||
#define SCSI_SENSE_NO_SENSE 0x00
|
||||
#define SCSI_SENSE_NO_INDEX 0x01
|
||||
#define SCSI_SENSE_SEEK_NOT_COMP 0x02
|
||||
#define SCSI_SENSE_WRITE_FAULT 0x03
|
||||
#define SCSI_SENSE_DRIVE_NOT_READY 0x04
|
||||
#define SCSI_SENSE_NO_TRACK0 0x06
|
||||
#define SCSI_SENSE_ID_CRC_ERROR 0x10
|
||||
#define SCSI_SENSE_UNCORRECTABLE 0x11
|
||||
#define SCSI_SENSE_ADDRESS_NF 0x12
|
||||
#define SCSI_SENSE_RECORD_NOT_FOUND 0x14
|
||||
#define SCSI_SENSE_SEEK_ERROR 0x15
|
||||
#define SCSI_SENSE_DATA_CHECK_RETRY 0x18
|
||||
#define SCSI_SENSE_ECC_VERIFY 0x19
|
||||
#define SCSI_SENSE_INTERLEAVE_ERROR 0x1A
|
||||
#define SCSI_SENSE_UNFORMATTED 0x1C
|
||||
#define SCSI_SENSE_ILLEGAL_COMMAND 0x20
|
||||
#define SCSI_SENSE_ILLEGAL_ADDRESS 0x21
|
||||
#define SCSI_SENSE_VOLUME_OVERFLOW 0x23
|
||||
#define SCSI_SENSE_BAD_ARGUMENT 0x24
|
||||
#define SCSI_SENSE_INVALID_LUN 0x25
|
||||
#define SCSI_SENSE_CART_CHANGED 0x28
|
||||
#define SCSI_SENSE_ERROR_OVERFLOW 0x2C
|
||||
|
||||
// SCSI IDs
|
||||
enum
|
||||
|
Loading…
Reference in New Issue
Block a user