/* Intel 8271 Floppy Disc Controller */
/* used in BBC Micro B,Acorn Atom */
/* Jun 2000. Kev Thacker */

/* TODO:

    - Scan commands
    - Check the commands work properly using a BBC disc copier program
    - check if 0 is specified as number of sectors, how many sectors
    is actually transfered
    - deleted data functions (error if data finds deleted data?)
*/


#include "emu.h"
#include "i8271.h"

/* data request */
#define I8271_FLAGS_DATA_REQUEST 0x01
/* data direction. If 0x02, then it is from fdc to cpu, else
it is from cpu to fdc */
#define I8271_FLAGS_DATA_DIRECTION 0x02

enum I8271_STATE_t
{
	I8271_STATE_EXECUTION_READ = 0,
	I8271_STATE_EXECUTION_WRITE
};

/* commands accepted */
#define I8271_COMMAND_SPECIFY                                       0x035
#define I8271_COMMAND_SEEK                                          0x029
#define I8271_COMMAND_READ_DRIVE_STATUS                             0x02c
#define I8271_COMMAND_READ_SPECIAL_REGISTER                         0x03d
#define I8271_COMMAND_WRITE_SPECIAL_REGISTER                        0x03a
#define I8271_COMMAND_FORMAT                                        0x023
#define I8271_COMMAND_READ_ID                                       0x01b
#define I8271_COMMAND_READ_DATA_SINGLE_RECORD                       0x012
#define I8271_COMMAND_READ_DATA_AND_DELETED_DATA_SINGLE_RECORD      0x016
#define I8271_COMMAND_WRITE_DATA_SINGLE_RECORD                      0x00a
#define I8271_COMMAND_WRITE_DELETED_DATA_SINGLE_RECORD              0x00e
#define I8271_COMMAND_VERIFY_DATA_AND_DELETED_DATA_SINGLE_RECORD    0x01e
#define I8271_COMMAND_READ_DATA_MULTI_RECORD                        0x013
#define I8271_COMMAND_READ_DATA_AND_DELETED_DATA_MULTI_RECORD       0x017
#define I8271_COMMAND_WRITE_DATA_MULTI_RECORD                       0x00b
#define I8271_COMMAND_WRITE_DELETED_DATA_MULTI_RECORD               0x00f
#define I8271_COMMAND_VERIFY_DATA_AND_DELETED_DATA_MULTI_RECORD     0x01f
#define I8271_COMMAND_SCAN_DATA                                     0x000
#define I8271_COMMAND_SCAN_DATA_AND_DELETED_DATA                    0x004

/*
#define I8271_COMMAND_READ_OPERATION                            (1<<4)
#define I8271_COMMAND_DELETED_DATA                              (1<<2)
#define I8271_COMMAND_MULTI_RECORD                              (1<<0)
*/



/* first parameter for specify command */
#define I8271_SPECIFY_INITIALIZATION                                0x0d
#define I8271_SPECIFY_LOAD_BAD_TRACKS_SURFACE_0                     0x010
#define I8271_SPECIFY_LOAD_BAD_TRACKS_SURFACE_1                     0x018

/* first parameter for read/write special register */
#define I8271_SPECIAL_REGISTER_SCAN_SECTOR_NUMBER                   0x06
#define I8271_SPECIAL_REGISTER_SCAN_MSB_OF_COUNT                    0x014
#define I8271_SPECIAL_REGISTER_SCAN_LSB_OF_COUNT                    0x013
#define I8271_SPECIAL_REGISTER_SURFACE_0_CURRENT_TRACK              0x012
#define I8271_SPECIAL_REGISTER_SURFACE_1_CURRENT_TRACK              0x01a
#define I8271_SPECIAL_REGISTER_MODE_REGISTER                        0x017
#define I8271_SPECIAL_REGISTER_DRIVE_CONTROL_OUTPUT_PORT            0x023
#define I8271_SPECIAL_REGISTER_DRIVE_CONTROL_INPUT_PORT             0x022
#define I8271_SPECIAL_REGISTER_SURFACE_0_BAD_TRACK_1                0x010
#define I8271_SPECIAL_REGISTER_SURFACE_0_BAD_TRACK_2                0x011
#define I8271_SPECIAL_REGISTER_SURFACE_1_BAD_TRACK_1                0x018
#define I8271_SPECIAL_REGISTER_SURFACE_1_BAD_TRACK_2                0x019


/* status register bits */
#define I8271_STATUS_COMMAND_BUSY   0x080
#define I8271_STATUS_COMMAND_FULL   0x040
#define I8271_STATUS_PARAMETER_FULL 0x020
#define I8271_STATUS_RESULT_FULL    0x010
#define I8271_STATUS_INT_REQUEST    0x008
#define I8271_STATUS_NON_DMA_REQUEST    0x004

#define VERBOSE 0

#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
#define FDC_LOG(x) do { if (VERBOSE) logerror("I8271: %s\n",x); } while (0)
#define FDC_LOG_COMMAND(x) do { if (VERBOSE) logerror("I8271: COMMAND %s\n",x); } while (0)


/* Device Interface */

const device_type I8271 = &device_creator<i8271_device>;

i8271_device::i8271_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
	: device_t(mconfig, I8271, "Intel 8271", tag, owner, clock, "i8271", __FILE__),
	m_write_irq(*this),
	m_write_drq(*this),
	m_floppy_tag1(NULL),
	m_floppy_tag2(NULL),
	m_flags(0),
	m_state(0),
	m_Command(0),
	m_StatusRegister(0),
	m_CommandRegister(0),
	m_ResultRegister(0),
	m_ParameterRegister(0),
	m_ResetRegister(0),
	m_data(0),
	m_ParameterCount(0),
	m_ParameterCountWritten(0),
	m_Mode(0),
	m_drive(0),
	m_side(0),
	m_drive_control_output(0),
	m_drive_control_input(0),
	m_StepRate(0),
	m_HeadSettlingTime(0),
	m_IndexCountBeforeHeadUnload(0),
	m_HeadLoadTime(0),
	//m_ID_C(0),
	//m_ID_H(0),
	m_ID_R(0),
	m_ID_N(0),
	m_data_id(0),
	m_ExecutionPhaseTransferCount(0),
	m_ExecutionPhaseCount(0),
	m_Counter(0)
	//m_data_direction(0)
{
	for (int i = 0; i < 8; i++ )
	{
		m_CommandParameters[i] = 0;
	}

	for (int i = 0; i < 2; i++ )
	{
		m_CurrentTrack[i] = 0;
	}

	for (int i = 0; i < 4; i++ )
	{
		m_BadTracks[i] = 0;
	}
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void i8271_device::device_start()
{
	m_write_irq.resolve_safe();
	m_write_drq.resolve_safe();

	m_data_timer = timer_alloc(TIMER_DATA_CALLBACK);
	m_command_complete_timer = timer_alloc(TIMER_TIMED_COMMAND_COMPLETE);
	m_drive = 0;
	m_pExecutionPhaseData = auto_alloc_array(machine(), char, 0x4000);

	m_floppy[0] = machine().device<legacy_floppy_image_device>(m_floppy_tag1);
	m_floppy[1] = machine().device<legacy_floppy_image_device>(m_floppy_tag2);

	// register for state saving
	/*save_item(NAME(m_flags));
	save_item(NAME(m_state));
	save_item(NAME(m_Command));
	save_item(NAME(m_StatusRegister));
	save_item(NAME(m_CommandRegister));
	save_item(NAME(m_ResultRegister));
	save_item(NAME(m_ParameterRegister));
	save_item(NAME(m_ResetRegister));
	save_item(NAME(m_data));
	//save_item(NAME(m_ParameterCount));
	//save_item(NAME(m_ParameterCountWritten));
	save_item(NAME(m_CommandParameters));
	//save_item(NAME(m_CurrentTrack));
	//save_item(NAME(m_BadTracks));
	//save_item(NAME(m_Mode));
	save_item(NAME(m_drive));
	save_item(NAME(m_side));
	save_item(NAME(m_drive_control_output));
	save_item(NAME(m_drive_control_input));
	//save_item(NAME(m_StepRate));
	//save_item(NAME(m_HeadSettlingTime));
	//save_item(NAME(m_IndexCountBeforeHeadUnload));
	//save_item(NAME(m_HeadLoadTime));
	save_item(NAME(m_ID_C));
	save_item(NAME(m_ID_H));
	save_item(NAME(m_ID_R));
	save_item(NAME(m_ID_N));
	save_item(NAME(m_data_id));
	save_item(NAME(m_ExecutionPhaseTransferCount));
	save_item(NAME(m_ExecutionPhaseCount));
	save_item(NAME(m_Counter));
	save_item(NAME(m_data_direction));
	save_pointer(NAME(m_pExecutionPhaseData), 0x4000);*/
}

//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void i8271_device::device_reset()
{
	m_StatusRegister = 0;  //I8271_STATUS_INT_REQUEST | I8271_STATUS_NON_DMA_REQUEST;
	m_Mode = 0x0c0; /* bits 0, 1 are initialized to zero */
	m_ParameterCountWritten = 0;
	m_ParameterCount = 0;

	/* if timer is active remove */
	m_command_complete_timer->reset();
	m_data_timer->reset();

	/* clear irq */
	set_irq_state(0);
	/* clear dma */
	set_dma_drq();
}

void i8271_device::seek_to_track(int track)
{
	if (track==0)
	{
		/* seek to track 0 */
		unsigned char StepCount = 0x0ff;

		/*logerror("step\n"); */

		/* track 0 not set, not seeked more than 255 tracks */
		while (m_floppy[m_drive]->floppy_tk00_r() && (StepCount != 0))
		{
/*            logerror("step\n"); */
			StepCount--;
			m_floppy[m_drive]->floppy_drive_seek(-1);
		}

		m_CurrentTrack[m_drive] = 0;

		/* failed to find track 0? */
		if (StepCount==0)
		{
			/* Completion Type: operator intervation probably required for recovery */
			/* Completion code: track 0 not found */
			m_ResultRegister |= (2<<3) | 2<<1;
		}

		/* step out - towards track 0 */
		m_drive_control_output &=~(1<<2);
	}
	else
	{
		signed int SignedTracks;

		/* calculate number of tracks to seek */
		SignedTracks = track - m_CurrentTrack[m_drive];

		/* step towards 0 */
		m_drive_control_output &= ~(1<<2);

		if (SignedTracks>0)
		{
			/* step away from 0 */
			m_drive_control_output |= (1<<2);
		}


		/* seek to track 0 */
		m_floppy[m_drive]->floppy_drive_seek(SignedTracks);

		m_CurrentTrack[m_drive] = track;
	}
}

void i8271_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
	case TIMER_DATA_CALLBACK:
		/* ok, trigger data request now */
		data_request();

		/* stop it */
		m_data_timer->reset();
		break;

	case TIMER_TIMED_COMMAND_COMPLETE:
		command_complete(1,1);

		/* stop it, but don't allow it to be free'd */
		m_command_complete_timer->reset();
		break;

	default:
		break;
	}
}

/* setup a timed data request - data request will be triggered in a few usecs time */
void i8271_device::timed_data_request()
{
	int usecs;
	/* 64 for single density */
	usecs = 64;

	/* set timers */
	m_command_complete_timer->reset();
	m_data_timer->adjust(attotime::from_usec(usecs));
}


/* setup a irq to occur 128us later - in reality this would be much later, because the int would
come after reading the two CRC bytes at least! This function is used when a irq is required at
command completion. Required for read data and write data, where last byte could be missed! */
void i8271_device::timed_command_complete()
{
	int usecs;

	/* 64 for single density - 2 crc bytes later*/
	usecs = 64*2;

	/* set timers */
	m_data_timer->reset();
	m_command_complete_timer->adjust(attotime::from_usec(usecs));
}

void i8271_device::set_irq_state(int state)
{
	m_StatusRegister &= ~I8271_STATUS_INT_REQUEST;
	if (state)
	{
		m_StatusRegister |= I8271_STATUS_INT_REQUEST;
	}

	m_write_irq((m_StatusRegister & I8271_STATUS_INT_REQUEST) ? ASSERT_LINE : CLEAR_LINE);
}

void i8271_device::set_dma_drq()
{
	m_write_drq((m_flags & I8271_FLAGS_DATA_REQUEST) ? 1 : 0);
}

void i8271_device::load_bad_tracks(int surface)
{
	m_BadTracks[(surface<<1) + 0] = m_CommandParameters[1];
	m_BadTracks[(surface<<1) + 1] = m_CommandParameters[2];
	m_CurrentTrack[surface] = m_CommandParameters[3];
}

void i8271_device::write_bad_track(int surface, int track, int data)
{
	m_BadTracks[(surface<<1) + (track-1)] = data;
}

void i8271_device::write_current_track(int surface, int track)
{
	m_CurrentTrack[surface] = track;
}

int i8271_device::read_current_track(int surface)
{
	return m_CurrentTrack[surface];
}

int i8271_device::read_bad_track(int surface, int track)
{
	return m_BadTracks[(surface<<1) + (track-1)];
}

void i8271_device::get_drive()
{
	/* &40 = drive 0 side 0 */
	/* &80 = drive 1 side 0 */



	if (m_CommandRegister & (1<<6))
	{
		m_drive = 0;
	}

	if (m_CommandRegister & (1<<7))
	{
		m_drive = 1;
	}

}

void i8271_device::check_all_parameters_written()
{
	if (m_ParameterCount == m_ParameterCountWritten)
	{
		m_StatusRegister &= ~I8271_STATUS_COMMAND_FULL;

		command_execute();
	}
}


void i8271_device::update_state()
{
	switch (m_state)
	{
		/* fdc reading data and passing it to cpu which must read it */
		case I8271_STATE_EXECUTION_READ:
		{
	//      /* if data request has been cleared, i.e. caused by a read of the register */
	//      if ((m_flags & I8271_FLAGS_DATA_REQUEST)==0)
			{
				/* setup data with byte */
				m_data = m_pExecutionPhaseData[m_ExecutionPhaseCount];

/*              logerror("read data %02x\n", m_data); */

				/* update counters */
				m_ExecutionPhaseCount++;
				m_ExecutionPhaseTransferCount--;

			//  logerror("Count: %04x\n", m_ExecutionPhaseCount);
			//  logerror("Remaining: %04x\n", m_ExecutionPhaseTransferCount);

				/* completed? */
				if (m_ExecutionPhaseTransferCount==0)
				{
					/* yes */

			//      logerror("sector read complete!\n");
					/* continue command */
					command_continue();
				}
				else
				{
					/* no */

					/* issue data request */
					timed_data_request();
				}
			}
		}
		break;

		/* fdc reading data and passing it to cpu which must read it */
		case I8271_STATE_EXECUTION_WRITE:
		{
			/* setup data with byte */
			m_pExecutionPhaseData[m_ExecutionPhaseCount] = m_data;
			/* update counters */
			m_ExecutionPhaseCount++;
			m_ExecutionPhaseTransferCount--;

			/* completed? */
			if (m_ExecutionPhaseTransferCount==0)
			{
				/* yes */

				/* continue command */
				command_continue();
			}
			else
			{
				/* no */

				/* issue data request */
				timed_data_request();
			}
		}
		break;

		default:
			break;
	}
}

void i8271_device::initialise_execution_phase_read(int transfer_size)
{
	/* read */
	m_flags |= I8271_FLAGS_DATA_DIRECTION;
	m_ExecutionPhaseCount = 0;
	m_ExecutionPhaseTransferCount = transfer_size;
	m_state = I8271_STATE_EXECUTION_READ;
}


void i8271_device::initialise_execution_phase_write(int transfer_size)
{
	/* write */
	m_flags &= ~I8271_FLAGS_DATA_DIRECTION;
	m_ExecutionPhaseCount = 0;
	m_ExecutionPhaseTransferCount = transfer_size;
	m_state = I8271_STATE_EXECUTION_WRITE;
}

/* for data transfers */
void i8271_device::data_request()
{
	m_flags |= I8271_FLAGS_DATA_REQUEST;

	if ((m_Mode & 0x01)!=0)
	{
		/* non-dma */
		m_StatusRegister |= I8271_STATUS_NON_DMA_REQUEST;
		/* set int */
		set_irq_state(1);
	}
	else
	{
		/* dma */
		m_StatusRegister &= ~I8271_STATUS_NON_DMA_REQUEST;

		set_dma_drq();
	}
}

void i8271_device::command_complete(int result, int int_rq)
{
	/* not busy, and not a execution phase data request in non-dma mode */
	m_StatusRegister &= ~(I8271_STATUS_COMMAND_BUSY | I8271_STATUS_NON_DMA_REQUEST);

	if (result)
	{
		m_StatusRegister |= I8271_STATUS_RESULT_FULL;
	}

	if (int_rq)
	{
		/* trigger an int */
		set_irq_state(1);
	}

	/* correct?? */
	m_drive_control_output &=~1;
}


/* for data transfers */
void i8271_device::clear_data_request()
{
	m_flags &= ~I8271_FLAGS_DATA_REQUEST;

	if ((m_Mode & 0x01)!=0)
	{
		/* non-dma */
		m_StatusRegister &= ~I8271_STATUS_NON_DMA_REQUEST;
		/* set int */
		set_irq_state(0);
	}
	else
	{
		/* dma */
		set_dma_drq();
	}
}


void i8271_device::command_continue()
{
	switch (m_Command)
	{
		case I8271_COMMAND_READ_DATA_MULTI_RECORD:
		case I8271_COMMAND_READ_DATA_SINGLE_RECORD:
		{
			/* completed all sectors? */
			m_Counter--;
			/* increment sector id */
			m_ID_R++;

			/* end command? */
			if (m_Counter==0)
			{
				timed_command_complete();
				return;
			}

			do_read();
		}
		break;

		case I8271_COMMAND_WRITE_DATA_MULTI_RECORD:
		case I8271_COMMAND_WRITE_DATA_SINGLE_RECORD:
		{
			/* put the buffer to the sector */
			m_floppy[m_drive]->floppy_drive_write_sector_data(m_side, m_data_id, m_pExecutionPhaseData, 1<<(m_ID_N+7),0);

			/* completed all sectors? */
			m_Counter--;
			/* increment sector id */
			m_ID_R++;

			/* end command? */
			if (m_Counter==0)
			{
				timed_command_complete();
				return;
			}

			do_write();
		}
		break;

		case I8271_COMMAND_READ_ID:
		{
			m_Counter--;

			if (m_Counter==0)
			{
				timed_command_complete();
				return;
			}

			do_read_id();
		}
		break;

		default:
			break;
	}
}

void i8271_device::do_read()
{
	/* find the sector */
	if (find_sector())
	{
		/* get the sector into the buffer */
		m_floppy[m_drive]->floppy_drive_read_sector_data(m_side, m_data_id, m_pExecutionPhaseData, 1<<(m_ID_N+7));

		/* initialise for reading */
		initialise_execution_phase_read(1<<(m_ID_N+7));

		/* update state - gets first byte and triggers a data request */
		timed_data_request();
		return;
	}
	LOG(("error getting sector data\n"));

	timed_command_complete();
}

void i8271_device::do_read_id()
{
	chrn_id id;

	/* get next id from disc */
	m_floppy[m_drive]->floppy_drive_get_next_id(m_side,&id);

	m_pExecutionPhaseData[0] = id.C;
	m_pExecutionPhaseData[1] = id.H;
	m_pExecutionPhaseData[2] = id.R;
	m_pExecutionPhaseData[3] = id.N;

	initialise_execution_phase_read(4);
}


void i8271_device::do_write()
{
	/* find the sector */
	if (find_sector())
	{
		/* initialise for reading */
		initialise_execution_phase_write(1<<(m_ID_N+7));

		/* update state - gets first byte and triggers a data request */
		timed_data_request();
		return;
	}
	LOG(("error getting sector data\n"));

	timed_command_complete();
}



int i8271_device::find_sector()
{
//  int track_count_attempt;

//  track_count_attempt
	/* find sector within one revolution of the disc - 2 index pulses */

	/* number of times we have seen index hole */
	int index_count = 0;

	/* get sector id's */
	do
	{
		chrn_id id;

		/* get next id from disc */
		if (m_floppy[m_drive]->floppy_drive_get_next_id(m_side,&id))
		{
			/* tested on Amstrad CPC - All bytes must match, otherwise
			a NO DATA error is reported */
			if (id.R == m_ID_R)
			{
				/* TODO: Is this correct? What about bad tracks? */
				if (id.C == m_CurrentTrack[m_drive])
				{
					m_data_id = id.data_id;
					return 1;
				}
				else
				{
					/* TODO: if track doesn't match, the real 8271 does a step */


					return 0;
				}
			}
		}

			/* index set? */
		if (m_floppy[m_drive]->floppy_drive_get_flag_state(FLOPPY_DRIVE_INDEX))
		{
			index_count++;
		}

	}
	while (index_count!=2);

	/* completion type: command/drive error */
	/* completion code: sector not found */
	m_ResultRegister |= (3<<3);

	return 0;
}

void i8271_device::command_execute()
{
	/* clear it = good completion status */
	/* this will be changed if anything bad happens! */
	m_ResultRegister = 0;

	switch (m_Command)
	{
		case I8271_COMMAND_SPECIFY:
		{
			switch (m_CommandParameters[0])
			{
				case 0x0d:
				{
					LOG(("Initialization\n"));
					m_StepRate = m_CommandParameters[1];
					m_HeadSettlingTime = m_CommandParameters[2];
					m_IndexCountBeforeHeadUnload = (m_CommandParameters[3]>>4) & 0x0f;
					m_HeadLoadTime = (m_CommandParameters[3] & 0x0f);
				}
				break;

				case 0x010:
				{
					LOG(("Load bad Tracks Surface 0\n"));
					load_bad_tracks(0);

				}
				break;

				case 0x018:
				{
					LOG(("Load bad Tracks Surface 1\n"));
					load_bad_tracks(1);

				}
				break;
			}

			/* no result */
			command_complete(0,0);
		}
		break;

		case I8271_COMMAND_READ_SPECIAL_REGISTER:
		{
			/* unknown - what is read when a special register that isn't allowed is specified? */
			int data = 0x0ff;

			switch (m_CommandParameters[0])
			{
				case I8271_SPECIAL_REGISTER_MODE_REGISTER:
				{
					data = m_Mode;
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_0_CURRENT_TRACK:
				{
					data = read_current_track(0);

				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_1_CURRENT_TRACK:
				{
					data = read_current_track(1);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_0_BAD_TRACK_1:
				{
					data = read_bad_track(0,1);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_0_BAD_TRACK_2:
				{
					data = read_bad_track(0,2);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_1_BAD_TRACK_1:
				{
					data = read_bad_track(1,1);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_1_BAD_TRACK_2:
				{
					data = read_bad_track(1,2);
				}
				break;

				case I8271_SPECIAL_REGISTER_DRIVE_CONTROL_OUTPUT_PORT:
				{
					FDC_LOG_COMMAND("Read Drive Control Output port\n");

					get_drive();

					/* assumption: select bits reflect the select bits from the previous
					command. i.e. read drive status */
					data = (m_drive_control_output & ~0x0c0) | (m_CommandRegister & 0x0c0);
				}
				break;

				case I8271_SPECIAL_REGISTER_DRIVE_CONTROL_INPUT_PORT:
				{
					/* bit 7: not used */
					/* bit 6: ready 1 */
					/* bit 5: write fault */
					/* bit 4: index */
					/* bit 3: wr prot */
					/* bit 2: rdy 0 */
					/* bit 1: track 0 */
					/* bit 0: cnt/opi */

					FDC_LOG_COMMAND("Read Drive Control Input port\n");


					m_drive_control_input = (1<<6) | (1<<2);

					/* bit 3 = 0 if write protected */
					m_drive_control_input |= m_floppy[m_drive]->floppy_wpt_r() << 3;

					/* bit 1 = 0 if head at track 0 */
					m_drive_control_input |= m_floppy[m_drive]->floppy_tk00_r() << 1;

					/* need to setup this register based on drive selected */
					data = m_drive_control_input;
				}
				break;

			}

			m_ResultRegister = data;

			command_complete(1,0);
		}
		break;


		case I8271_COMMAND_WRITE_SPECIAL_REGISTER:
		{
			switch (m_CommandParameters[0])
			{
				case I8271_SPECIAL_REGISTER_MODE_REGISTER:
				{
					/* TODO: Check bits 6-7 and 5-2 are valid */
					m_Mode = m_CommandParameters[1];

					if (m_Mode & 0x01)
					{
						LOG(("Mode: Non-DMA\n"));
					}
					else
					{
						LOG(("Mode: DMA\n"));
					}

					if (m_Mode & 0x02)
					{
						LOG(("Single actuator\n"));
					}
					else
					{
						LOG(("Double actuator\n"));
					}
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_0_CURRENT_TRACK:
				{
					LOG(("Surface 0 Current Track\n"));
					write_current_track(0, m_CommandParameters[1]);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_1_CURRENT_TRACK:
				{
					LOG(("Surface 1 Current Track\n"));
					write_current_track(1, m_CommandParameters[1]);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_0_BAD_TRACK_1:
				{
					LOG(("Surface 0 Bad Track 1\n"));
					write_bad_track(0, 1, m_CommandParameters[1]);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_0_BAD_TRACK_2:
				{
					LOG(("Surface 0 Bad Track 2\n"));
					write_bad_track(0, 2,m_CommandParameters[1]);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_1_BAD_TRACK_1:
				{
					LOG(("Surface 1 Bad Track 1\n"));


					write_bad_track(1, 1, m_CommandParameters[1]);
				}
				break;

				case I8271_SPECIAL_REGISTER_SURFACE_1_BAD_TRACK_2:
				{
					LOG(("Surface 1 Bad Track 2\n"));

					write_bad_track(1, 2, m_CommandParameters[1]);
				}
				break;

				case I8271_SPECIAL_REGISTER_DRIVE_CONTROL_OUTPUT_PORT:
				{
//                  /* get drive selected */
//                  m_drive = (m_CommandParameters[1]>>6) & 0x03;

					FDC_LOG_COMMAND("Write Drive Control Output port\n");


					if (m_CommandParameters[1] & 0x01)
					{
						LOG(("Write Enable\n"));
					}
					if (m_CommandParameters[1] & 0x02)
					{
						LOG(("Seek/Step\n"));
					}
					if (m_CommandParameters[1] & 0x04)
					{
						LOG(("Direction\n"));
					}
					if (m_CommandParameters[1] & 0x08)
					{
						LOG(("Load Head\n"));
					}
					if (m_CommandParameters[1] & 0x010)
					{
						LOG(("Low head current\n"));
					}
					if (m_CommandParameters[1] & 0x020)
					{
						LOG(("Write Fault Reset\n"));
					}

					LOG(("Select %02x\n", (m_CommandParameters[1] & 0x0c0)>>6));

					/* get drive */
					get_drive();

					/* on bbc dfs 09 this is the side select output */
					m_side = (m_CommandParameters[1]>>5) & 0x01;

					/* load head - on mini-sized drives this turns on the disc motor,
					on standard-sized drives this loads the head and turns the motor on */
					m_floppy[m_drive]->floppy_mon_w(!BIT(m_CommandParameters[1], 3));
					m_floppy[m_drive]->floppy_drive_set_ready_state(1, 1);

					/* step pin changed? if so perform a step in the direction indicated */
					if (((m_drive_control_output^m_CommandParameters[1]) & (1<<1))!=0)
					{
						/* step pin changed state? */

						if (BIT(m_CommandParameters[1], 1))
						{
							signed int signed_tracks;

							if (BIT(m_CommandParameters[1], 2))
							{
								signed_tracks = 1;
							}
							else
							{
								signed_tracks = -1;
							}

							m_floppy[m_drive]->floppy_drive_seek(signed_tracks);
						}
					}

					m_drive_control_output = m_CommandParameters[1];


				}
				break;

				case I8271_SPECIAL_REGISTER_DRIVE_CONTROL_INPUT_PORT:
				{
					FDC_LOG_COMMAND("Write Drive Control Input port\n");

					//                  m_drive_control_input = m_CommandParameters[1];
				}
				break;

			}

			/* write doesn't supply a result */
			command_complete(0,0);
		}
		break;

		case I8271_COMMAND_READ_DRIVE_STATUS:
		{
			unsigned char status;

			get_drive();

			/* no write fault */
			status = 0;

			status |= (1<<2) | (1<<6);

			/* these two do not appear to be set at all! ?? */

			if (m_floppy[0])
			{
				if (m_floppy[0]->floppy_drive_get_flag_state(FLOPPY_DRIVE_READY))
				{
					status |= (1 << 2);
				}
			}

			if (m_floppy[1])
			{
				if (m_floppy[1]->floppy_drive_get_flag_state(FLOPPY_DRIVE_READY))
				{
					status |= (1 << 6);
				}
			}

			/* bit 3 = 1 if write protected */
			status |= !m_floppy[m_drive]->floppy_wpt_r() << 3;

			/* bit 1 = 1 if head at track 0 */
			status |= !m_floppy[m_drive]->floppy_tk00_r() << 1;

			m_ResultRegister = status;
			command_complete(1,0);

		}
		break;

		case I8271_COMMAND_SEEK:
		{
			get_drive();

			seek_to_track(m_CommandParameters[0]);

			/* check for bad seek */
			timed_command_complete();

		}
		break;

		case I8271_COMMAND_READ_DATA_MULTI_RECORD:
		{
			/* N value as stored in ID field */
			m_ID_N = (m_CommandParameters[2]>>5) & 0x07;

			/* starting sector id */
			m_ID_R = m_CommandParameters[1];

			/* number of sectors to transfer */
			m_Counter = m_CommandParameters[2] & 0x01f;


			FDC_LOG_COMMAND("READ DATA MULTI RECORD");

			LOG(("Sector Count: %02x\n", m_Counter));
			LOG(("Track: %02x\n",m_CommandParameters[0]));
			LOG(("Sector: %02x\n", m_CommandParameters[1]));
			LOG(("Sector Length: %02x bytes\n", 1<<(m_ID_N+7)));

			get_drive();

			if (!m_floppy[m_drive]->floppy_drive_get_flag_state(FLOPPY_DRIVE_READY))
			{
				/* Completion type: operation intervention probably required for recovery */
				/* Completion code: Drive not ready */
				m_ResultRegister = (2<<3);
				timed_command_complete();
			}
			else
			{
				seek_to_track(m_CommandParameters[0]);


				do_read();
			}

		}
		break;

		case I8271_COMMAND_READ_DATA_SINGLE_RECORD:
		{
			FDC_LOG_COMMAND("READ DATA SINGLE RECORD");

			m_ID_N = 0;
			m_Counter = 1;
			m_ID_R = m_CommandParameters[1];

			LOG(("Sector Count: %02x\n", m_Counter));
			LOG(("Track: %02x\n",m_CommandParameters[0]));
			LOG(("Sector: %02x\n", m_CommandParameters[1]));
			LOG(("Sector Length: %02x bytes\n", 1<<(m_ID_N+7)));

			get_drive();

			if (!m_floppy[m_drive]->floppy_drive_get_flag_state(FLOPPY_DRIVE_READY))
			{
				/* Completion type: operation intervention probably required for recovery */
				/* Completion code: Drive not ready */
				m_ResultRegister = (2<<3);
				timed_command_complete();
			}
			else
			{
				seek_to_track(m_CommandParameters[0]);

				do_read();
			}

		}
		break;

		case I8271_COMMAND_WRITE_DATA_MULTI_RECORD:
		{
			/* N value as stored in ID field */
			m_ID_N = (m_CommandParameters[2]>>5) & 0x07;

			/* starting sector id */
			m_ID_R = m_CommandParameters[1];

			/* number of sectors to transfer */
			m_Counter = m_CommandParameters[2] & 0x01f;

			FDC_LOG_COMMAND("READ DATA MULTI RECORD");

			LOG(("Sector Count: %02x\n", m_Counter));
			LOG(("Track: %02x\n",m_CommandParameters[0]));
			LOG(("Sector: %02x\n", m_CommandParameters[1]));
			LOG(("Sector Length: %02x bytes\n", 1<<(m_ID_N+7)));

			get_drive();

			m_drive_control_output &=~1;

			if (!m_floppy[m_drive]->floppy_drive_get_flag_state(FLOPPY_DRIVE_READY))
			{
				/* Completion type: operation intervention probably required for recovery */
				/* Completion code: Drive not ready */
				m_ResultRegister = (2<<3);
				timed_command_complete();
			}
			else
			{
				if (m_floppy[m_drive]->floppy_wpt_r() == CLEAR_LINE)
				{
					/* Completion type: operation intervention probably required for recovery */
					/* Completion code: Drive write protected */
					m_ResultRegister = (2<<3) | (1<<1);
					timed_command_complete();
				}
				else
				{
					m_drive_control_output |=1;

					seek_to_track(m_CommandParameters[0]);

					do_write();
				}
			}
		}
		break;

		case I8271_COMMAND_WRITE_DATA_SINGLE_RECORD:
		{
			FDC_LOG_COMMAND("WRITE DATA SINGLE RECORD");

			m_ID_N = 0;
			m_Counter = 1;
			m_ID_R = m_CommandParameters[1];


			LOG(("Sector Count: %02x\n", m_Counter));
			LOG(("Track: %02x\n",m_CommandParameters[0]));
			LOG(("Sector: %02x\n", m_CommandParameters[1]));
			LOG(("Sector Length: %02x bytes\n", 1<<(m_ID_N+7)));
			get_drive();

			m_drive_control_output &=~1;

			if (!m_floppy[m_drive]->floppy_drive_get_flag_state(FLOPPY_DRIVE_READY))
			{
				/* Completion type: operation intervention probably required for recovery */
				/* Completion code: Drive not ready */
				m_ResultRegister = (2<<3);
				timed_command_complete();
			}
			else
			{
				if (m_floppy[m_drive]->floppy_wpt_r() == CLEAR_LINE)
				{
					/* Completion type: operation intervention probably required for recovery */
					/* Completion code: Drive write protected */
					m_ResultRegister = (2<<3) | (1<<1);
					timed_command_complete();
				}
				else
				{
					m_drive_control_output |=1;

					seek_to_track(m_CommandParameters[0]);

					do_write();
				}
			}

		}
		break;


		case I8271_COMMAND_READ_ID:
		{
			FDC_LOG_COMMAND("READ ID");

			LOG(("Track: %02x\n",m_CommandParameters[0]));
			LOG(("ID Field Count: %02x\n", m_CommandParameters[2]));

			get_drive();

			if (!m_floppy[m_drive]->floppy_drive_get_flag_state(FLOPPY_DRIVE_READY))
			{
				/* Completion type: operation intervention probably required for recovery */
				/* Completion code: Drive not ready */
				m_ResultRegister = (2<<3);
				timed_command_complete();
			}
			else
			{
				m_Counter = m_CommandParameters[2];

				seek_to_track(m_CommandParameters[0]);

				do_read_id();
			}
		}
		break;

		default:
			LOG(("ERROR Unrecognised Command\n"));
			break;
	}
}



WRITE8_MEMBER(i8271_device::write)
{
	switch (offset & 3)
	{
		case 0:
		{
			LOG(("I8271 W Command Register: %02x\n", data));

			m_CommandRegister = data;
			m_Command = m_CommandRegister & 0x03f;

			m_StatusRegister |= I8271_STATUS_COMMAND_BUSY | I8271_STATUS_COMMAND_FULL;
			m_StatusRegister &= ~I8271_STATUS_PARAMETER_FULL | I8271_STATUS_RESULT_FULL;
			m_ParameterCountWritten = 0;

			switch (m_Command)
			{
				case I8271_COMMAND_SPECIFY:
				{
					FDC_LOG_COMMAND("SPECIFY");

					m_ParameterCount = 4;
				}
				break;

				case I8271_COMMAND_SEEK:
				{
					FDC_LOG_COMMAND("SEEK");

					m_ParameterCount = 1;
				}
				break;

				case I8271_COMMAND_READ_DRIVE_STATUS:
				{
					FDC_LOG_COMMAND("READ DRIVE STATUS");

					m_ParameterCount = 0;
				}
				break;

				case I8271_COMMAND_READ_SPECIAL_REGISTER:
				{
					FDC_LOG_COMMAND("READ SPECIAL REGISTER");

					m_ParameterCount = 1;
				}
				break;

				case I8271_COMMAND_WRITE_SPECIAL_REGISTER:
				{
					FDC_LOG_COMMAND("WRITE SPECIAL REGISTER");

					m_ParameterCount = 2;
				}
				break;

				case I8271_COMMAND_FORMAT:
				{
					m_ParameterCount = 5;
				}
				break;

				case I8271_COMMAND_READ_ID:
				{
					m_ParameterCount = 3;

				}
				break;


				case I8271_COMMAND_READ_DATA_SINGLE_RECORD:
				case I8271_COMMAND_READ_DATA_AND_DELETED_DATA_SINGLE_RECORD:
				case I8271_COMMAND_WRITE_DATA_SINGLE_RECORD:
				case I8271_COMMAND_WRITE_DELETED_DATA_SINGLE_RECORD:
				case I8271_COMMAND_VERIFY_DATA_AND_DELETED_DATA_SINGLE_RECORD:
				{
					m_ParameterCount = 2;
				}
				break;

				case I8271_COMMAND_READ_DATA_MULTI_RECORD:
				case I8271_COMMAND_READ_DATA_AND_DELETED_DATA_MULTI_RECORD:
				case I8271_COMMAND_WRITE_DATA_MULTI_RECORD:
				case I8271_COMMAND_WRITE_DELETED_DATA_MULTI_RECORD:
				case I8271_COMMAND_VERIFY_DATA_AND_DELETED_DATA_MULTI_RECORD:
				{
					m_ParameterCount = 3;
				}
				break;

				case I8271_COMMAND_SCAN_DATA:
				case I8271_COMMAND_SCAN_DATA_AND_DELETED_DATA:
				{
					m_ParameterCount = 5;
				}
				break;






			}

			check_all_parameters_written();
		}
		break;

		case 1:
		{
			LOG(("I8271 W Parameter Register: %02x\n",data));
			m_ParameterRegister = data;

			if (m_ParameterCount!=0)
			{
				m_CommandParameters[m_ParameterCountWritten] = data;
				m_ParameterCountWritten++;
			}

			check_all_parameters_written();
		}
		break;

		case 2:
		{
			LOG(("I8271 W Reset Register: %02x\n", data));
			if (((data ^ m_ResetRegister) & 0x01)!=0)
			{
				if ((data & 0x01)==0)
				{
					reset();
				}
			}

			m_ResetRegister = data;


		}
		break;

		default:
			break;
	}
}

READ8_MEMBER(i8271_device::read)
{
	switch (offset & 3)
	{
		case 0:
		{
			/* bit 1,0 are zero other bits contain status data */
			m_StatusRegister &= ~0x03;
			LOG(("I8271 R Status Register: %02x\n",m_StatusRegister));
			return m_StatusRegister;
		}

		case 1:
		{
			if ((m_StatusRegister & I8271_STATUS_COMMAND_BUSY)==0)
			{
				/* clear IRQ */
				set_irq_state(0);

				m_StatusRegister &= ~I8271_STATUS_RESULT_FULL;
				LOG(("I8271 R Result Register %02x\n",m_ResultRegister));
				return m_ResultRegister;
			}

			/* not useful information when command busy */
			return 0x0ff;
		}


		default:
			break;
	}

	return 0x0ff;
}


/* to be completed! */
READ8_MEMBER(i8271_device::dack_r)
{
	return data_r(space, offset);
}

/* to be completed! */
WRITE8_MEMBER(i8271_device::dack_w)
{
	data_w(space, offset, data);
}

READ8_MEMBER(i8271_device::data_r)
{
	clear_data_request();

	update_state();

	//  logerror("I8271 R data: %02x\n",m_data);


	return m_data;
}

WRITE8_MEMBER(i8271_device::data_w)
{
	m_data = data;

//    logerror("I8271 W data: %02x\n",m_data);

	clear_data_request();

	update_state();
}
