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:
smf- 2012-10-07 22:54:59 +00:00
parent fd98bf8bde
commit 95dcf93a69
8 changed files with 837 additions and 856 deletions

View File

@ -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>;

View File

@ -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;

View File

@ -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>;

View File

@ -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;

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}

View File

@ -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