diff options
Diffstat (limited to 'trunk/src/emu/machine/scsihd.c')
-rw-r--r-- | trunk/src/emu/machine/scsihd.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/scsihd.c b/trunk/src/emu/machine/scsihd.c new file mode 100644 index 00000000000..283937dcdd6 --- /dev/null +++ b/trunk/src/emu/machine/scsihd.c @@ -0,0 +1,338 @@ +/*************************************************************************** + + scsihd.c - Implementation of a SCSI hard disk drive + +***************************************************************************/ + +#include "emu.h" +#include "scsidev.h" +#include "harddisk.h" +#include "imagedev/harddriv.h" +#include "scsihd.h" + +typedef struct +{ + UINT32 lba; + UINT32 blocks; + hard_disk_file *disk; + bool is_file; +} SCSIHd; + + +// scsihd_exec_command + +static int scsihd_exec_command( SCSIInstance *scsiInstance, UINT8 *statusCode ) +{ + UINT8 *command; + int commandLength; + SCSIHd *our_this = (SCSIHd *)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 = 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; + + case 0x2a: // WRITE (10) + our_this->lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5]; + our_this->blocks = SCSILengthFromUINT16( &command[7] ); + + 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 0xa8: // READ(12) + 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; + + 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 = (SCSIHd *)SCSIThis( &SCSIClassHARDDISK, scsiInstance ); + SCSIGetCommand( scsiInstance, &command, &commandLength ); + + // if we're a drive without a disk, return all zeroes + if (!our_this->disk) + { + memset(data, 0, dataLength); + return; + } + + 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 = (SCSIHd *)SCSIThis( &SCSIClassHARDDISK, scsiInstance ); + SCSIGetCommand( scsiInstance, &command, &commandLength ); + + if (!our_this->disk) + { + return; + } + + switch ( command[0] ) + { + case 0x0a: // WRITE(6) + case 0x2a: // WRITE(10) + 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, const char *diskregion ) +{ + running_machine &machine = scsiInstance->machine(); + SCSIHd *our_this = (SCSIHd *)SCSIThis( &SCSIClassHARDDISK, scsiInstance ); + + our_this->lba = 0; + our_this->blocks = 0; + + state_save_register_item( machine, "scsihd", diskregion, 0, our_this->lba ); + state_save_register_item( machine, "scsihd", diskregion, 0, our_this->blocks ); + + if (machine.device( diskregion )) { + our_this->is_file = TRUE; + our_this->disk = machine.device<harddisk_image_device>(diskregion)->get_hard_disk_file(); + } else { + our_this->is_file = FALSE; + our_this->disk = hard_disk_open(get_disk_handle( machine, diskregion )); + } + + if (!our_this->disk) + { + logerror("SCSIHD: no HD found!\n"); + } +} + +static void scsihd_delete_instance( SCSIInstance *scsiInstance ) +{ + SCSIHd *our_this = (SCSIHd *)SCSIThis( &SCSIClassHARDDISK, scsiInstance ); + if (!our_this->is_file) { + if( our_this->disk ) + { + hard_disk_close( our_this->disk ); + } + } +} + +static void scsihd_get_device( SCSIInstance *scsiInstance, hard_disk_file **disk ) +{ + SCSIHd *our_this = (SCSIHd *)SCSIThis( &SCSIClassHARDDISK, scsiInstance ); + *disk = our_this->disk; +} + +static void scsihd_set_device( SCSIInstance *scsiInstance, hard_disk_file *disk ) +{ + SCSIHd *our_this = (SCSIHd *)SCSIThis( &SCSIClassHARDDISK, scsiInstance ); + our_this->disk = disk; +} + +static int scsihd_dispatch(int operation, void *file, INT64 intparm, void *ptrparm) +{ + SCSIAllocInstanceParams *params; + + switch (operation) + { + case SCSIOP_EXEC_COMMAND: + return scsihd_exec_command( (SCSIInstance *)file, (UINT8 *)ptrparm ); + + case SCSIOP_READ_DATA: + scsihd_read_data( (SCSIInstance *)file, (UINT8 *)ptrparm, intparm ); + return 0; + + case SCSIOP_WRITE_DATA: + scsihd_write_data( (SCSIInstance *)file, (UINT8 *)ptrparm, intparm ); + return 0; + + case SCSIOP_ALLOC_INSTANCE: + params = (SCSIAllocInstanceParams *)ptrparm; + SCSIBase( &SCSIClassHARDDISK, operation, (SCSIInstance *)file, intparm, (UINT8 *)ptrparm ); + scsihd_alloc_instance( params->instance, params->diskregion ); + return 0; + + case SCSIOP_DELETE_INSTANCE: + scsihd_delete_instance( (SCSIInstance *)file ); + break; + + case SCSIOP_GET_DEVICE: + scsihd_get_device( (SCSIInstance *)file, (hard_disk_file **)ptrparm ); + return 0; + + case SCSIOP_SET_DEVICE: + scsihd_set_device( (SCSIInstance *)file, (hard_disk_file *)ptrparm ); + return 0; + } + + return SCSIBase( &SCSIClassHARDDISK, operation, (SCSIInstance *)file, intparm, (UINT8 *)ptrparm ); +} + +const SCSIClass SCSIClassHARDDISK = +{ + &SCSIClassDevice, + scsihd_dispatch, + sizeof( SCSIHd ) +}; |