summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/scsihd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/scsihd.c')
-rw-r--r--src/emu/machine/scsihd.c311
1 files changed, 311 insertions, 0 deletions
diff --git a/src/emu/machine/scsihd.c b/src/emu/machine/scsihd.c
new file mode 100644
index 00000000000..81d3770eb71
--- /dev/null
+++ b/src/emu/machine/scsihd.c
@@ -0,0 +1,311 @@
+/***************************************************************************
+
+ scsihd.c - Implementation of a SCSI hard disk drive
+
+***************************************************************************/
+
+#include "driver.h"
+#include "scsidev.h"
+#include "harddisk.h"
+
+#ifdef MESS
+#include "devices/harddriv.h"
+#endif
+
+typedef struct
+{
+ UINT32 lba;
+ UINT32 blocks;
+ hard_disk_file *disk;
+} SCSIHd;
+
+
+// scsihd_exec_command
+
+static int scsihd_exec_command( SCSIInstance *scsiInstance, UINT8 *statusCode )
+{
+ UINT8 *command;
+ int commandLength;
+ SCSIHd *our_this = SCSIThis( &SCSIClassHARDDISK, scsiInstance );
+ SCSIGetCommand( scsiInstance, &command, &commandLength );
+
+ switch ( command[0] )
+ {
+ case 0x03: // REQUEST SENSE
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x04: // FORMAT UNIT
+ SCSISetPhase( scsiInstance, SCSI_PHASE_STATUS );
+ return 0;
+
+ case 0x08: // READ(6)
+ our_this->lba = (command[1]&0x1f)<<16 | command[2]<<8 | command[3];
+ our_this->blocks = SCSILengthFromUINT8( &command[4] );
+
+ logerror("SCSIHD: READ at LBA %x for %x blocks\n", our_this->lba, our_this->blocks);
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return our_this->blocks * 512;
+
+ case 0x0a: // WRITE(6)
+ our_this->lba = (command[1]&0x1f)<<16 | command[2]<<8 | command[3];
+ our_this->blocks = SCSILengthFromUINT8( &command[4] );
+
+ logerror("SCSIHD: WRITE to LBA %x for %x blocks\n", our_this->lba, our_this->blocks);
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAOUT );
+ return our_this->blocks * 512;
+
+ case 0x12: // INQUIRY
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x15: // MODE SELECT (used to set CDDA volume)
+ logerror("SCSIHD: MODE SELECT length %x control %x\n", command[4], command[5]);
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAOUT );
+ return SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x1a: // MODE SENSE(6)
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x25: // READ CAPACITY
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return 8;
+
+ case 0x28: // READ(10)
+ our_this->lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
+ our_this->blocks = command[6]<<24 | command[7]<<16 | command[8]<<8 | command[9];
+
+ logerror("SCSIHD: READ at LBA %x for %x blocks\n", our_this->lba, our_this->blocks);
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return our_this->blocks * 512;
+
+ case 0xa8: // READ(12)
+ our_this->lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
+ our_this->blocks = SCSILengthFromUINT16( &command[7] );
+
+ logerror("SCSIHD: READ at LBA %x for %x blocks\n", our_this->lba, our_this->blocks);
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return our_this->blocks * 512;
+
+ default:
+ return SCSIBase( &SCSIClassHARDDISK, SCSIOP_EXEC_COMMAND, scsiInstance, 0, NULL );
+ }
+}
+
+static void scsihd_read_data( SCSIInstance *scsiInstance, UINT8 *data, int dataLength )
+{
+ int i;
+ UINT8 *command;
+ int commandLength;
+ SCSIHd *our_this = SCSIThis( &SCSIClassHARDDISK, scsiInstance );
+ SCSIGetCommand( scsiInstance, &command, &commandLength );
+
+ switch ( command[0] )
+ {
+ case 0x03: // REQUEST SENSE
+ data[0] = 0x80; // valid sense
+ for (i = 1; i < 12; i++)
+ {
+ data[i] = 0;
+ }
+ break;
+
+ case 0x12: // INQUIRY
+ memset( data, 0, dataLength );
+ data[0] = 0x00; // device is direct-access (e.g. hard disk)
+ data[1] = 0x00; // media is not removable
+ data[2] = 0x05; // device complies with SPC-3 standard
+ data[3] = 0x02; // response data format = SPC-3 standard
+ // Apple HD SC setup utility needs to see this
+ strcpy((char *)&data[8], " SEAGATE");
+ strcpy((char *)&data[16], " ST225N");
+ strcpy((char *)&data[32], "1.0");
+ break;
+
+ case 0x1a: // MODE SENSE (6 byte)
+ // special Apple ID page. this is a vendor-specific page,
+ // so unless collisions occur there should be no need
+ // to change it.
+ if ((command[2] & 0x3f) == 0x30)
+ {
+ memset(data, 0, 40);
+ data[0] = 0x14;
+ strcpy((char *)&data[14], "APPLE COMPUTER, INC.");
+ }
+ break;
+
+ case 0x08: // READ(6)
+ case 0x28: // READ(10)
+ case 0xa8: // READ(12)
+ if ((our_this->disk) && (our_this->blocks))
+ {
+ while (dataLength > 0)
+ {
+ if (!hard_disk_read(our_this->disk, our_this->lba, data))
+ {
+ logerror("SCSIHD: HD read error!\n");
+ }
+ our_this->lba++;
+ our_this->blocks--;
+ dataLength -= 512;
+ data += 512;
+ }
+ }
+ break;
+
+
+ case 0x25: // READ CAPACITY
+ {
+ hard_disk_info *info;
+ UINT32 temp;
+
+ info = hard_disk_get_info(our_this->disk);
+
+ logerror("SCSIHD: READ CAPACITY\n");
+
+ // get # of sectors
+ temp = info->cylinders * info->heads * info->sectors;
+ temp--;
+
+ data[0] = (temp>>24) & 0xff;
+ data[1] = (temp>>16) & 0xff;
+ data[2] = (temp>>8) & 0xff;
+ data[3] = (temp & 0xff);
+ data[4] = (info->sectorbytes>>24)&0xff;
+ data[5] = (info->sectorbytes>>16)&0xff;
+ data[6] = (info->sectorbytes>>8)&0xff;
+ data[7] = (info->sectorbytes & 0xff);
+ }
+ break;
+
+ default:
+ SCSIBase( &SCSIClassHARDDISK, SCSIOP_READ_DATA, scsiInstance, dataLength, data );
+ break;
+ }
+}
+
+static void scsihd_write_data( SCSIInstance *scsiInstance, UINT8 *data, int dataLength )
+{
+ UINT8 *command;
+ int commandLength;
+ SCSIHd *our_this = SCSIThis( &SCSIClassHARDDISK, scsiInstance );
+ SCSIGetCommand( scsiInstance, &command, &commandLength );
+
+ switch ( command[0] )
+ {
+ case 0x0a: // WRITE(6)
+ if ((our_this->disk) && (our_this->blocks))
+ {
+ while (dataLength > 0)
+ {
+ if (!hard_disk_write(our_this->disk, our_this->lba, data))
+ {
+ logerror("SCSIHD: HD write error!\n");
+ }
+ our_this->lba++;
+ our_this->blocks--;
+ dataLength -= 512;
+ data += 512;
+ }
+ }
+ break;
+
+ default:
+ SCSIBase( &SCSIClassHARDDISK, SCSIOP_WRITE_DATA, scsiInstance, dataLength, data );
+ break;
+ }
+}
+
+static void scsihd_alloc_instance( SCSIInstance *scsiInstance, int diskId )
+{
+ SCSIHd *our_this = SCSIThis( &SCSIClassHARDDISK, scsiInstance );
+
+ our_this->lba = 0;
+ our_this->blocks = 0;
+
+ state_save_register_item( "scsihd", diskId, our_this->lba );
+ state_save_register_item( "scsihd", diskId, our_this->blocks );
+
+#ifdef MESS
+ our_this->disk = mess_hd_get_hard_disk_file_by_number( diskId );
+#else
+ our_this->disk = hard_disk_open(get_disk_handle( diskId ));
+
+ if (!our_this->disk)
+ {
+ logerror("SCSIHD: no HD found!\n");
+ }
+#endif
+}
+
+static void scsihd_delete_instance( SCSIInstance *scsiInstance )
+{
+#ifndef MESS
+ SCSIHd *our_this = SCSIThis( &SCSIClassHARDDISK, scsiInstance );
+
+ if( our_this->disk )
+ {
+ hard_disk_close( our_this->disk );
+ }
+#endif
+}
+
+static void scsihd_get_device( SCSIInstance *scsiInstance, hard_disk_file **disk )
+{
+ SCSIHd *our_this = SCSIThis( &SCSIClassHARDDISK, scsiInstance );
+ *disk = our_this->disk;
+}
+
+static void scsihd_set_device( SCSIInstance *scsiInstance, hard_disk_file *disk )
+{
+ SCSIHd *our_this = SCSIThis( &SCSIClassHARDDISK, scsiInstance );
+ our_this->disk = disk;
+}
+
+static int scsihd_dispatch(int operation, void *file, INT64 intparm, void *ptrparm)
+{
+ switch (operation)
+ {
+ case SCSIOP_EXEC_COMMAND:
+ return scsihd_exec_command( file, ptrparm );
+
+ case SCSIOP_READ_DATA:
+ scsihd_read_data( file, ptrparm, intparm );
+ return 0;
+
+ case SCSIOP_WRITE_DATA:
+ scsihd_write_data( file, ptrparm, intparm );
+ return 0;
+
+ case SCSIOP_ALLOC_INSTANCE:
+ SCSIBase( &SCSIClassHARDDISK, operation, file, intparm, ptrparm );
+ scsihd_alloc_instance( *((SCSIInstance **) ptrparm), intparm );
+ return 0;
+
+ case SCSIOP_DELETE_INSTANCE:
+ scsihd_delete_instance( file );
+ break;
+
+ case SCSIOP_GET_DEVICE:
+ scsihd_get_device( file, ptrparm );
+ return 0;
+
+ case SCSIOP_SET_DEVICE:
+ scsihd_set_device( file, ptrparm );
+ return 0;
+ }
+
+ return SCSIBase( &SCSIClassHARDDISK, operation, file, intparm, ptrparm );
+}
+
+SCSIClass SCSIClassHARDDISK =
+{
+ &SCSIClassDevice,
+ scsihd_dispatch,
+ sizeof( SCSIHd )
+};