// license:BSD-3-Clause
// copyright-holders:smf
#include "emu.h"
#include "t10sbc.h"

void t10sbc::t10_start(device_t &device)
{
	m_device = &device;
	t10spc::t10_start(device);

	device.save_item( NAME( m_lba ) );
	device.save_item( NAME( m_blocks ) );
}

void t10sbc::t10_reset()
{
	t10spc::t10_reset();

	m_lba = 0;
	m_blocks = 0;
	m_sector_bytes = 512;

	m_disk = m_image->get_hard_disk_file();
	if (!m_disk)
	{
		m_device->logerror("T10SBC %s: no HD found!\n", m_image->owner()->tag());
	}
	else
	{
		// get hard disk sector size from CHD metadata
		const hard_disk_info *hdinfo = hard_disk_get_info(m_disk);
		m_sector_bytes = hdinfo->sectorbytes;
	}
}

// scsihd_exec_command
void t10sbc::ExecCommand()
{
	switch ( command[0] )
	{
	case T10SBC_CMD_FORMAT_UNIT:
		m_phase = SCSI_PHASE_STATUS;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = 0;
		break;

	case T10SBC_CMD_SEEK_6:
		m_lba = (command[1]&0x1f)<<16 | command[2]<<8 | command[3];

		m_device->logerror("S1410: SEEK to LBA %x\n", m_lba);

		m_phase = SCSI_PHASE_STATUS;
		m_transfer_length = 0;
		break;

	case T10SBC_CMD_READ_6:
		m_lba = (command[1]&0x1f)<<16 | command[2]<<8 | command[3];
		m_blocks = SCSILengthFromUINT8( &command[4] );

		m_device->logerror("T10SBC: READ at LBA %x for %x blocks\n", m_lba, m_blocks);

		m_phase = SCSI_PHASE_DATAIN;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = m_blocks * m_sector_bytes;
		break;

	case T10SBC_CMD_WRITE_6:
		m_lba = (command[1]&0x1f)<<16 | command[2]<<8 | command[3];
		m_blocks = SCSILengthFromUINT8( &command[4] );

		m_device->logerror("T10SBC: WRITE to LBA %x for %x blocks\n", m_lba, m_blocks);

		m_phase = SCSI_PHASE_DATAOUT;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = m_blocks * m_sector_bytes;
		break;

	case T10SPC_CMD_INQUIRY:
		m_phase = SCSI_PHASE_DATAIN;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = SCSILengthFromUINT8( &command[ 4 ] );
		break;

	case T10SPC_CMD_MODE_SELECT_6:
		m_device->logerror("T10SBC: MODE SELECT length %x control %x\n", command[4], command[5]);
		m_phase = SCSI_PHASE_DATAOUT;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = SCSILengthFromUINT8( &command[ 4 ] );
		break;

	case T10SPC_CMD_MODE_SENSE_6:
		m_phase = SCSI_PHASE_DATAIN;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = SCSILengthFromUINT8( &command[ 4 ] );
		break;

	case T10SBC_CMD_READ_CAPACITY:
		m_phase = SCSI_PHASE_DATAIN;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = 8;
		break;

	case T10SBC_CMD_READ_10:
		m_lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
		m_blocks = SCSILengthFromUINT16( &command[7] );

		m_device->logerror("T10SBC: READ at LBA %x for %x blocks\n", m_lba, m_blocks);

		m_phase = SCSI_PHASE_DATAIN;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = m_blocks * m_sector_bytes;
		break;

	case T10SBC_CMD_WRITE_10:
		m_lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
		m_blocks = SCSILengthFromUINT16( &command[7] );

		m_device->logerror("T10SBC: WRITE to LBA %x for %x blocks\n", m_lba, m_blocks);

		m_phase = SCSI_PHASE_DATAOUT;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = m_blocks * m_sector_bytes;
		break;

	case T10SBC_CMD_READ_12:
		m_lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
		m_blocks = command[6]<<24 | command[7]<<16 | command[8]<<8 | command[9];

		m_device->logerror("T10SBC: READ at LBA %x for %x blocks\n", m_lba, m_blocks);

		m_phase = SCSI_PHASE_DATAIN;
		m_status_code = SCSI_STATUS_CODE_GOOD;
		m_transfer_length = m_blocks * m_sector_bytes;
		break;

	default:
		t10spc::ExecCommand();
		break;
	}
}

void t10sbc::ReadData( uint8_t *data, int dataLength )
{
	// if we're a drive without a disk, return all zeroes
	if (!m_disk)
	{
		memset(data, 0, dataLength);
		return;
	}

	switch ( command[0] )
	{
	case T10SPC_CMD_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 T10SPC_CMD_MODE_SENSE_6:
		// 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 T10SBC_CMD_READ_6:
	case T10SBC_CMD_READ_10:
	case T10SBC_CMD_READ_12:
		if ((m_disk) && (m_blocks))
		{
			while (dataLength > 0)
			{
				if (!hard_disk_read(m_disk, m_lba,  data))
				{
					m_device->logerror("T10SBC: HD read error!\n");
				}
				m_lba++;
				m_blocks--;
				dataLength -= m_sector_bytes;
				data += m_sector_bytes;
			}
		}
		break;

	case T10SBC_CMD_READ_CAPACITY: // READ CAPACITY
		{
			hard_disk_info *info;
			uint32_t temp;

			info = hard_disk_get_info(m_disk);

			m_device->logerror("T10SBC: 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:
		t10spc::ReadData( data, dataLength );
		break;
	}
}

void t10sbc::WriteData( uint8_t *data, int dataLength )
{
	if (!m_disk)
	{
		return;
	}

	switch ( command[0] )
	{
	case T10SPC_CMD_MODE_SELECT_6:
		break;

	case T10SBC_CMD_WRITE_6:
	case T10SBC_CMD_WRITE_10:
		if ((m_disk) && (m_blocks))
		{
			while (dataLength > 0)
			{
				if (!hard_disk_write(m_disk, m_lba, data))
				{
					m_device->logerror("T10SBC: HD write error!\n");
				}
				m_lba++;
				m_blocks--;
				dataLength -= m_sector_bytes;
				data += m_sector_bytes;
			}
		}
		break;

	default:
		t10spc::WriteData( data, dataLength );
		break;
	}
}

void t10sbc::GetDevice( void **_disk )
{
	*(hard_disk_file **)_disk = m_disk;
}

void t10sbc::SetDevice( void *_disk )
{
	m_disk = (hard_disk_file *)_disk;
}
