From 95dcf93a69b4240488fecb39f77363eefa405f86 Mon Sep 17 00:00:00 2001 From: smf- Date: Sun, 7 Oct 2012 22:54:59 +0000 Subject: [PATCH] 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] --- src/emu/machine/scsibus.c | 650 +------------------------------------- src/emu/machine/scsibus.h | 127 +------- src/emu/machine/scsicb.c | 120 +++++-- src/emu/machine/scsicb.h | 55 ++-- src/emu/machine/scsidev.c | 22 +- src/emu/machine/scsidev.h | 31 +- src/emu/machine/scsihle.c | 592 +++++++++++++++++++++++++++++++++- src/emu/machine/scsihle.h | 96 ++++-- 8 files changed, 837 insertions(+), 856 deletions(-) diff --git a/src/emu/machine/scsibus.c b/src/emu/machine/scsibus.c index 309b653c00d..12ccd5e7767 100644 --- a/src/emu/machine/scsibus.c +++ b/src/emu/machine/scsibus.c @@ -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= 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; break; - case SCSI_LINE_SEL: result=(linestate & (1<> SCSI_LINE_SEL; break; - case SCSI_LINE_CD: result=(linestate & (1<> SCSI_LINE_CD; break; - case SCSI_LINE_IO: result=(linestate & (1<> SCSI_LINE_IO; break; - case SCSI_LINE_MSG: result=(linestate & (1<> SCSI_LINE_MSG; break; - case SCSI_LINE_REQ: result=(linestate & (1<> SCSI_LINE_REQ; break; - case SCSI_LINE_ACK: result=(linestate & (1<> SCSI_LINE_ACK; break; - case SCSI_LINE_ATN: result=(linestate & (1<> SCSI_LINE_MSG; break; - case SCSI_LINE_RST: result=(linestate & (1<> 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<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<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(device); + scsidev_device *scsidev = dynamic_cast(device); if( scsidev != NULL ) { - devices[scsidev->GetDeviceID()] = scsidev; - } - else - { - scsicb_device *scsicb = dynamic_cast(device); - m_scsicb = scsicb; + devices[ deviceCount++ ] = scsidev; } } + + data = SCSI_MASK_ALL; } const device_type SCSIBUS = &device_creator; diff --git a/src/emu/machine/scsibus.h b/src/emu/machine/scsibus.h index ac13634b0eb..25bb50429d6 100644 --- a/src/emu/machine/scsibus.h +++ b/src/emu/machine/scsibus.h @@ -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; diff --git a/src/emu/machine/scsicb.c b/src/emu/machine/scsicb.c index 5d583c91ef6..affe48e5fd0 100644 --- a/src/emu/machine/scsicb.c +++ b/src/emu/machine/scsicb.c @@ -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( 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( 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( 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( 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; diff --git a/src/emu/machine/scsicb.h b/src/emu/machine/scsicb.h index fc55bd6f2cc..8466176e2c6 100644 --- a/src/emu/machine/scsicb.h +++ b/src/emu/machine/scsicb.h @@ -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; diff --git a/src/emu/machine/scsidev.c b/src/emu/machine/scsidev.c index 409d9f8739d..eb9df37fa3a 100644 --- a/src/emu/machine/scsidev.c +++ b/src/emu/machine/scsidev.c @@ -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( owner() ); + m_scsibus->scsi_update(); +} diff --git a/src/emu/machine/scsidev.h b/src/emu/machine/scsidev.h index 623cdf929a5..689666a5e10 100644 --- a/src/emu/machine/scsidev.h +++ b/src/emu/machine/scsidev.h @@ -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 diff --git a/src/emu/machine/scsihle.c b/src/emu/machine/scsihle.c index 2818d3f87ba..d599d540e08 100644 --- a/src/emu/machine/scsihle.c +++ b/src/emu/machine/scsihle.c @@ -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(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= 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; +} diff --git a/src/emu/machine/scsihle.h b/src/emu/machine/scsihle.h index aa20d91e699..427b7e9f887 100644 --- a/src/emu/machine/scsihle.h +++ b/src/emu/machine/scsihle.h @@ -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