/***************************************************************************

    machine/upd765.c

    Functions to emulate a UPD765/Intel 8272 compatible floppy disk controller

    Code by Kevin Thacker.

    TODO:

    - overrun condition
    - Scan Commands
    - crc error in id field and crc error in data field errors
    - disc not present, and no sectors on track for data, deleted data, write, write deleted,
        read a track etc
        - end of cylinder condition - almost working, needs fixing  with
                PCW and PC drivers
    - resolve "ready" state stuff (ready state when reset for PC, ready state change while processing command AND
    while idle)

    Changes:
    091006 (Mariusz Wojcieszek, changes needed by QX-10):
    - allowed "Sense Interrupt Status" command when Seek is active
    - DIO bit in status register (0x40) is cleared when "Read Data" command is executed,
      it is later set during result phase.

***************************************************************************/

#include "emu.h"
#include "machine/upd765.h"


typedef enum
{
	UPD765_COMMAND_PHASE_FIRST_BYTE,
	UPD765_COMMAND_PHASE_BYTES,
	UPD765_RESULT_PHASE,
	UPD765_EXECUTION_PHASE_READ,
	UPD765_EXECUTION_PHASE_WRITE
} UPD765_PHASE;

/* supported versions */
typedef enum
{
	TYPE_UPD765A = 0,
	TYPE_UPD765B = 1,
	TYPE_SMC37C78 = 2,
	TYPE_UPD72065 = 3
} UPD765_VERSION;


/* uncomment the following line for verbose information */
#define LOG_VERBOSE		0
#define LOG_COMMAND		0
#define LOG_EXTRA		0
#define LOG_INTERRUPT	0

/* uncomment this to not allow end of cylinder "error" */
#define NO_END_OF_CYLINDER



/* state of upd765 Interrupt (INT) output */
#define UPD765_INT	0x02
/* data rate for floppy discs (MFM data) */
#define UPD765_DATA_RATE	32
/* state of upd765 terminal count input*/
#define UPD765_TC	0x04

#define UPD765_DMA_MODE 0x08

#define UPD765_SEEK_OPERATION_IS_RECALIBRATE 0x01

#define UPD765_SEEK_ACTIVE 0x010
/* state of upd765 DMA DRQ output */
#define UPD765_DMA_DRQ 0x020
/* state of upd765 FDD READY input */
#define UPD765_FDD_READY 0x040

#define UPD765_MF	0x40

#define UPD765_RESET 0x080

#define UPD765_BAD_MEDIA 0x100

typedef struct _upd765_t upd765_t;
struct _upd765_t
{
	devcb_resolved_write_line	out_int_func;
	devcb_resolved_write_line	out_drq_func;

	unsigned long	sector_counter;
	/* version of fdc to emulate */
	UPD765_VERSION version;

	/* main status register */
	unsigned char    FDC_main;
	/* data register */
	unsigned char	upd765_data_reg;

	unsigned char c,h,r,n;

	int sector_id;

	int data_type;

	char format_data[4];

	UPD765_PHASE    upd765_phase;
	unsigned int    upd765_command_bytes[16];
	unsigned int    upd765_result_bytes[16];
	unsigned int    upd765_transfer_bytes_remaining;
	unsigned int    upd765_transfer_bytes_count;
	unsigned int    upd765_status[4];
	/* present cylinder number per drive */
	unsigned int    pcn[4];

	/* drive being accessed. drive outputs from fdc */
	unsigned int    drive;
	/* side being accessed: side output from fdc */
	unsigned int	side;


	/* step rate time in us */
	unsigned long	srt_in_ms;

	unsigned int	ncn;

//  unsigned int    upd765_id_index;
	char *execution_phase_data;
	unsigned int	upd765_flags;

//  unsigned char specify[2];
//  unsigned char perpendicular_mode[1];

	int command;

	emu_timer *seek_timer;
	emu_timer *timer;
	int timer_type;

	emu_timer *command_timer;

	char *data_buffer;
	const upd765_interface *intf;
};

//static void upd765_setup_data_request(unsigned char Data);
static void upd765_setup_command(device_t *device);
static TIMER_CALLBACK(upd765_continue_command);
static int upd765_sector_count_complete(device_t *device);
static void upd765_increment_sector(device_t *device);
static void upd765_update_state(device_t *device);
static void upd765_set_dma_drq(device_t *device,int state);
static void upd765_set_int(device_t *device,int state);

static const INT8 upd765_cmd_size[32] =
{
	1,1,9,3,2,9,9,2,1,9,2,1,9,6,1,3,
	1,9,1,1,1,1,9,1,1,9,1,1,1,9,1,1
};

INLINE upd765_t *get_safe_token(device_t *device)
{
	assert(device != NULL);
	assert(device->type() == UPD765A || device->type() == UPD765B ||
		device->type() == SMC37C78 || device->type() == UPD72065);

	return (upd765_t *)downcast<legacy_device_base *>(device)->token();
}

static device_t *current_image(device_t *device)
{
	device_t *image = NULL;
	upd765_t *fdc = get_safe_token(device);

	if (!fdc->intf->get_image)
	{
		if (fdc->intf->floppy_drive_tags[fdc->drive] != NULL)
		{
			if (device->owner() != NULL)
				image = device->owner()->subdevice(fdc->intf->floppy_drive_tags[fdc->drive]);
			else
				image = device->machine().device(fdc->intf->floppy_drive_tags[fdc->drive]);
		}
	}
	else
	{
		image = fdc->intf->get_image(device, fdc->drive);
	}
	return image;
}

static void upd765_setup_drive_and_side(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	/* drive index upd765 sees */
	fdc->drive = fdc->upd765_command_bytes[1] & 0x03;
	/* side index upd765 sees */
	fdc->side = (fdc->upd765_command_bytes[1]>>2) & 0x01;
}


/* setup status register 0 based on data in status register 1 and 2 */
static void upd765_setup_st0(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	/* clear completition status bits, drive bits and side bits */
	fdc->upd765_status[0] &= ~((1<<7) | (1<<6) | (1<<2) | (1<<1) | (1<<0));
	/* fill in drive */
	fdc->upd765_status[0] |= fdc->drive | (fdc->side<<2);

	/* fill in completion status bits based on bits in st0, st1, st2 */
	/* no error bits set */
	if ((fdc->upd765_status[1] | fdc->upd765_status[2])==0)
	{
		return;
	}

	fdc->upd765_status[0] |= 0x040;
}


static int upd765_n_to_bytes(int n)
{
	/* 0-> 128 bytes, 1->256 bytes, 2->512 bytes etc */
	/* data_size = ((1<<(N+7)) */
	return 1<<(n+7);
}

static void upd765_set_data_request(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	fdc->FDC_main |= 0x080;
}

static void upd765_clear_data_request(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	fdc->FDC_main &= ~0x080;
}

static int upd765_get_rdy(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);

	if (fdc->intf->rdy_pin == UPD765_RDY_PIN_CONNECTED)
	{
		device_t *img = current_image(device);
		if (img!=NULL) {
			return floppy_drive_get_flag_state(img, FLOPPY_DRIVE_READY);
		} else {
			return 0;
		}
	}
	else
		return 1;
}

static void upd765_seek_complete(device_t *device)
{
	/* tested on Amstrad CPC */

	/* if a seek is done without drive connected: */
	/*  abnormal termination of command,
        seek complete,
        not ready
    */

	/* if a seek is done with drive connected, but disc missing: */
	/* abnormal termination of command,
        seek complete,
        not ready */

	/* if a seek is done with drive connected and disc in drive */
	/* seek complete */


	/* On the PC however, it appears that recalibrates and seeks can be performed without
    a disc in the drive. */

	/* Therefore, the above output is dependant on the state of the drive */

	/* In the Amstrad CPC, the drive select is provided by the UPD765. A single port is also
    assigned for setting the drive motor state. The motor state controls the motor of the selected
    drive */

	/* On the PC the drive can be selected with the DIGITAL OUTPUT REGISTER, and the motor of each
    of the 4 possible drives is also settable using the same register */

	/* Assumption for PC: (NOT TESTED - NEEDS VERIFICATION) */

	/* If a seek is done without drive connected: */
	/* abnormal termination of command,
        seek complete,
        fault
        */

	/* if a seek is done with drive connected, but disc missing: */
	/* seek complete */

	/* if a seek is done with drive connected and disc in drive: */
	/* seek complete */

	/* On Amstrad CPC:
        If drive not connected, or drive connected but disc not in drive, not ready!
        If drive connected and drive motor on, ready!
       On PC:
        Drive is always ready!

        In 37c78 docs, the ready bits of the upd765 are marked as unused.
        This indicates it is always ready!!!!!
    */

	device_t *img = current_image(device);
	upd765_t *fdc = get_safe_token(device);

	fdc->pcn[fdc->drive] = fdc->ncn;

	fdc->upd765_status[0] = 0x20;

	/* drive ready? */
	if (img != NULL && upd765_get_rdy(device))
	{
		/* recalibrate? */
		if (fdc->upd765_flags & UPD765_SEEK_OPERATION_IS_RECALIBRATE)
		{
			/* not at track 0? */
			if (fdc->pcn[fdc->drive] != 0)
				/* no, track 0 failed after 77 steps */
				fdc->upd765_status[0] |= 0x40 | 0x10;
		}
	}
	else
	{
		/* abnormal termination, not ready */
		fdc->upd765_status[0] |= 0x40 | 0x08;
	}

	/* set drive and side. note: commented out side to avoid problems with the tf20 */
	fdc->upd765_status[0] |= fdc->drive; //| (fdc->side<<2);

	upd765_set_int(device,0);
	upd765_set_int(device,1);

	fdc->upd765_flags &= ~UPD765_SEEK_ACTIVE;

	upd765_idle(device);
}

static TIMER_CALLBACK(upd765_seek_timer_callback)
{
	device_t *device = (device_t *)ptr;
	upd765_t *fdc = get_safe_token(device);
	/* seek complete */
	upd765_seek_complete(device);

	fdc->seek_timer->reset();
}

static void upd765_timer_func(device_t *device, int timer_type)
{
	upd765_t *fdc = get_safe_token(device);
	/* type 0 = data transfer mode in execution phase */
	if (fdc->timer_type == 0)
	{
		/* set data request */
		upd765_set_data_request(device);

		fdc->timer_type = 4;

		if (!(fdc->upd765_flags & UPD765_DMA_MODE))
		{
			if (fdc->upd765_command_bytes[0] & UPD765_MF)
			{
				/* MFM */
				fdc->timer->reset(attotime::from_usec(13));
			}
			else
			{
				/* FM */
				fdc->timer->reset(attotime::from_usec(27));
			}
		}
		else
		{
			upd765_timer_func(device, fdc->timer_type);
		}
	}
	else if (fdc->timer_type==2)
	{
		/* result phase begin */

		/* generate a int for specific commands */
		switch (fdc->command) {
		case 2:		/* read a track */
		case 5:		/* write data */
		case 6:		/* read data */
		case 9:		/* write deleted data */
		case 10:	/* read id */
		case 12:	/* read deleted data */
		case 13:	/* format at track */
		case 17:	/* scan equal */
		case 19:	/* scan low or equal */
		case 29:	/* scan high or equal */
			upd765_set_int(device,1);
			break;

		default:
			break;
		}

		upd765_set_data_request(device);

		fdc->timer->reset();
	}
	else if (fdc->timer_type == 4)
	{
		/* if in dma mode, a int is not generated per byte. If not in  DMA mode
        a int is generated per byte */
		if (fdc->upd765_flags & UPD765_DMA_MODE)
		{
			upd765_set_dma_drq(device,1);
		}
		else
		{
			if (fdc->FDC_main & (1<<7))
			{
				/* set int to indicate data is ready */
				upd765_set_int(device,1);
			}
		}

		fdc->timer->reset();
	}
}

static TIMER_CALLBACK(upd765_timer_callback)
{
	device_t *device = (device_t *)ptr;
	upd765_timer_func(device,param);
}

/* after (32-27) the DRQ is set, then 27 us later, the int is set.
I don't know if this is correct, but it is required for the PCW driver.
In this driver, the first NMI calls the handler function, furthur NMI's are
effectively disabled by reading the data before the NMI int can be set.
*/

static void upd765_setup_timed_generic(device_t *device, int timer_type, attotime duration)
{
	upd765_t *fdc = get_safe_token(device);

	fdc->timer_type = timer_type;

	if (!(fdc->upd765_flags & UPD765_DMA_MODE))
	{
		fdc->timer->adjust(duration);
	}
	else
	{
		upd765_timer_func(device,fdc->timer_type);
		fdc->timer->reset();
	}
}

/* setup data request */
static void upd765_setup_timed_data_request(device_t *device, int bytes)
{
	/* setup timer to trigger in UPD765_DATA_RATE us */
	upd765_setup_timed_generic(device, 0, attotime::from_usec(32-27)	/*UPD765_DATA_RATE)*bytes*/);
}

/* setup result data request */
static void upd765_setup_timed_result_data_request(device_t *device)
{
	upd765_setup_timed_generic(device, 2, attotime::from_usec(UPD765_DATA_RATE*2));
}


/* sets up a timer to issue a seek complete in signed_tracks time */
static void upd765_setup_timed_int(device_t *device,int signed_tracks)
{
	upd765_t *fdc = get_safe_token(device);
	/* setup timer to signal after seek time is complete */
	fdc->seek_timer->adjust(attotime::from_double(fdc->srt_in_ms*abs(signed_tracks)*0.001));
}

static void upd765_seek_setup(device_t *device, int is_recalibrate)
{
	device_t *img;
	int signed_tracks;
	upd765_t *fdc = get_safe_token(device);

	fdc->upd765_flags |= UPD765_SEEK_ACTIVE;

	if (is_recalibrate)
	{
		/* head cannot be specified with recalibrate */
		fdc->upd765_command_bytes[1] &=~0x04;
	}

	upd765_setup_drive_and_side(device);

	img = current_image(device);

	fdc->FDC_main |= (1<<fdc->drive);
	fdc->FDC_main |= 0x20;  // execution phase
	fdc->FDC_main &= ~0x10;  // not busy, can send another seek/recalibrate
	                         // for a different drive, or sense int status

	/* recalibrate command? */
	if (is_recalibrate)
	{
		fdc->upd765_flags |= UPD765_SEEK_OPERATION_IS_RECALIBRATE;

		fdc->ncn = 0;

		/* if drive is already at track 0, or drive is not ready */
		if (img == NULL || floppy_tk00_r(img) == CLEAR_LINE || (!upd765_get_rdy(device)))
		{
			/* seek completed */
//          upd765_seek_complete(device);
			// delay for the time of 1 step, the PCW does not like immediate recalibrates
			upd765_setup_timed_int(device,1);
		}
		else
		{
			/* is drive present? */
			if (1) //image_slotexists(img)) //fix me
			{
				/* yes - calculate real number of tracks to seek */

				int current_track;

				/* get current track */
				current_track = floppy_drive_get_current_track(img);

				/* get number of tracks to seek */
				signed_tracks = -current_track;
			}
			else
			{
				/* no, seek 77 tracks and then stop */
				/* true for UPD765A, but not for other variants */
				signed_tracks = -77;
			}

			/* perform seek - if drive isn't present it will not do anything */
			floppy_drive_seek(img, signed_tracks);

			if (signed_tracks!=0)
			{
				upd765_setup_timed_int(device,signed_tracks);
			}
			else
			{
				upd765_seek_complete(device);
			}
		}
	}
	else
	{

		fdc->upd765_flags &= ~UPD765_SEEK_OPERATION_IS_RECALIBRATE;

		fdc->ncn = fdc->upd765_command_bytes[2];

		/* get signed tracks */
		signed_tracks = fdc->ncn - fdc->pcn[fdc->drive];

		/* perform seek - if drive isn't present it will not do anything */
		floppy_drive_seek(img, signed_tracks);

		/* if no tracks to seek, or drive is not ready, seek is complete */
		if (img == NULL || (signed_tracks==0) || (!upd765_get_rdy(device)))
		{
			upd765_seek_complete(device);
		}
		else
		{
			/* seek complete - issue an interrupt */
			upd765_setup_timed_int(device,signed_tracks);
		}
	}
//    upd765_idle(device);
}



static void upd765_setup_execution_phase_read(device_t *device, char *ptr, int size)
{
	upd765_t *fdc = get_safe_token(device);

//  fdc->FDC_main &= ~0x040;                     /* FDC->CPU */
	fdc->FDC_main |= 0x040;                      /* FDC->CPU */

	fdc->upd765_transfer_bytes_count = 0;
	fdc->upd765_transfer_bytes_remaining = size;
	fdc->execution_phase_data = ptr;
	fdc->upd765_phase = UPD765_EXECUTION_PHASE_READ;

	upd765_setup_timed_data_request(device, 1);
}

static void upd765_setup_execution_phase_write(device_t *device, char *ptr, int size)
{
	upd765_t *fdc = get_safe_token(device);

	fdc->FDC_main &= ~0x040;                     /* FDC->CPU */

	fdc->upd765_transfer_bytes_count = 0;
	fdc->upd765_transfer_bytes_remaining = size;
	fdc->execution_phase_data = ptr;
	fdc->upd765_phase = UPD765_EXECUTION_PHASE_WRITE;

	/* setup a data request with first byte */
	upd765_setup_timed_data_request(device,1);
}


static void upd765_setup_result_phase(device_t *device, int byte_count)
{
	upd765_t *fdc = get_safe_token(device);

	fdc->FDC_main |= 0x040;                     /* FDC->CPU */
	fdc->FDC_main &= ~0x020;                    /* not execution phase */

	fdc->upd765_transfer_bytes_count = 0;
	fdc->upd765_transfer_bytes_remaining = byte_count;
	fdc->upd765_phase = UPD765_RESULT_PHASE;

	upd765_setup_timed_result_data_request(device);
}

void upd765_idle(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);

	fdc->FDC_main &= ~0x040;                     /* CPU->FDC */
	fdc->FDC_main &= ~0x020;                    /* not execution phase */
	fdc->FDC_main &= ~0x010;                     /* not busy */
	fdc->upd765_phase = UPD765_COMMAND_PHASE_FIRST_BYTE;

	upd765_set_data_request(device);
}



/* change flags */
static void upd765_change_flags(device_t *device,unsigned int flags, unsigned int mask)
{
	unsigned int new_flags;
	unsigned int changed_flags;
	upd765_t *fdc = get_safe_token(device);

	assert((flags & ~mask) == 0);

	/* compute the new flags and which ones have changed */
	new_flags = fdc->upd765_flags & ~mask;
	new_flags |= flags;
	changed_flags = fdc->upd765_flags ^ new_flags;
	fdc->upd765_flags = new_flags;

	/* if interrupt changed, call the handler */
	if (changed_flags & UPD765_INT)
		fdc->out_int_func((fdc->upd765_flags & UPD765_INT) ? 1 : 0);

	/* if DRQ changed, call the handler */
	if (changed_flags & UPD765_DMA_DRQ)
		fdc->out_drq_func((fdc->upd765_flags & UPD765_DMA_DRQ) ? 1 : 0);
}



/* set int output */
static void upd765_set_int(device_t *device, int state)
{
	if (LOG_INTERRUPT)
		logerror("upd765_set_int(): state=%d\n", state);
	upd765_change_flags(device, state ? UPD765_INT : 0, UPD765_INT);
}



/* set dma request output */
static void upd765_set_dma_drq(device_t *device, int state)
{
	upd765_change_flags(device, state ? UPD765_DMA_DRQ : 0, UPD765_DMA_DRQ);
}



/* Drive ready */

/*

A drive will report ready if:
- drive is selected
- disc is in the drive
- disk is rotating at a constant speed (normally 300rpm)

On more modern PCs, a ready signal is not provided by the drive.
This signal is not used in the PC design and was eliminated to save costs
If you look at the datasheets for the modern UPD765 variants, you will see the Ready
signal is not mentioned.

On the original UPD765A, ready signal is required, and some commands will fail if the drive
is not ready.




*/




/* done when ready state of drive changes */
/* this ignores if command is active, in which case command should terminate immediatly
with error */
static void upd765_set_ready_change_callback(device_t *controller, device_t *img, int state)
{
	upd765_t *fdc = get_safe_token(controller);
	int drive = floppy_get_drive(img);

	if (LOG_EXTRA)
		logerror("upd765: ready state change\n");

	/* drive that changed state */
	fdc->upd765_status[0] = 0x0c0 | drive;

	/* not ready */
	if (state==0 && fdc->intf->rdy_pin == UPD765_RDY_PIN_CONNECTED )
		fdc->upd765_status[0] |= 8;

	/* trigger an int */
	upd765_set_int(controller, 1);
}


/* terminal count input */
WRITE_LINE_DEVICE_HANDLER( upd765_tc_w )
{
	int old_state;
	upd765_t *fdc = get_safe_token(device);

	old_state = fdc->upd765_flags;

	/* clear drq */
	upd765_set_dma_drq(device, 0);

	fdc->upd765_flags &= ~UPD765_TC;
	if (state)
	{
		fdc->upd765_flags |= UPD765_TC;
	}

	/* changed state? */
	if (((fdc->upd765_flags^old_state) & UPD765_TC)!=0)
	{
		/* now set? */
		if ((fdc->upd765_flags & UPD765_TC)!=0)
		{
			/* yes */
			if (fdc->timer)
			{
				if (fdc->timer_type==0)
				{
					fdc->timer->reset();


				}
			}

#ifdef NO_END_OF_CYLINDER
			fdc->command_timer->adjust(attotime::zero);
#else
			upd765_update_state(device);
#endif
		}
	}
}

READ8_DEVICE_HANDLER( upd765_status_r )
{
	upd765_t *fdc = get_safe_token(device);
	if (LOG_EXTRA)
		logerror("%s: upd765_status_r: %02x\n", device->machine().describe_context(), fdc->FDC_main);
	return fdc->FDC_main;
}


/* control mark handling code */

/* if SK==1, and we are executing a read data command, and a deleted data mark is found,
skip it.
if SK==1, and we are executing a read deleted data command, and a data mark is found,
skip it. */

static int upd765_read_skip_sector(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	/* skip set? */
	if ((fdc->upd765_command_bytes[0] & (1<<5))!=0)
	{
		/* read data? */
		if (fdc->command == 0x06)
		{
			/* did we just find a sector with deleted data mark? */
			if (fdc->data_type == UPD765_DAM_DELETED_DATA)
			{
				/* skip it */
				return TRUE;
			}
		}
		/* deleted data? */
		else
		if (fdc->command == 0x0c)
		{
			/* did we just find a sector with data mark ? */
			if (fdc->data_type == UPD765_DAM_DATA)
			{
				/* skip it */
				return TRUE;
			}
		}
	}

	/* do not skip */
	return FALSE;
}

/* this is much closer to how the upd765 actually gets sectors */
/* used by read data, read deleted data, write data, write deleted data */
/* What the upd765 does:

  - get next sector id from disc
  - if sector id matches id specified in command, it will
    search for next data block and read data from it.

  - if the index is seen twice while it is searching for a sector, then the sector cannot be found
*/

static void upd765_get_next_id(device_t *device, chrn_id *id)
{
	upd765_t *fdc = get_safe_token(device);
	device_t *img = current_image(device);

	/* get next id from disc */
	floppy_drive_get_next_id(img, fdc->side,id);

	fdc->sector_id = id->data_id;

	/* set correct data type */
	fdc->data_type = UPD765_DAM_DATA;
	if (id->flags & ID_FLAG_DELETED_DATA)
	{
		fdc->data_type = UPD765_DAM_DELETED_DATA;
	}
}

static int upd765_get_matching_sector(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	device_t *img = current_image(device);
	chrn_id id;

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

	if (fdc->upd765_flags & UPD765_BAD_MEDIA) {
		fdc->upd765_status[1] |= 1;
		return FALSE;
	}

	/* get sector id's */
	do
    {
		upd765_get_next_id(device, &id);

		/* tested on Amstrad CPC - All bytes must match, otherwise
        a NO DATA error is reported */
		if (id.R == fdc->upd765_command_bytes[4])
		{
			if (id.C == fdc->upd765_command_bytes[2])
			{
				if (id.H == fdc->upd765_command_bytes[3])
				{
					if (id.N == fdc->upd765_command_bytes[5])
					{
						/* end of cylinder is set if:
                        1. sector data is read completely (i.e. no other errors occur like
                        no data.
                        2. sector being read is same specified by EOT
                        3. terminal count is not received */
						if (fdc->upd765_command_bytes[4]==fdc->upd765_command_bytes[6])
						{
							/* set end of cylinder */
							fdc->upd765_status[1] |= UPD765_ST1_END_OF_CYLINDER;
						}

						return TRUE;
					}
				}
			}
			else
			{
				/* the specified sector ID was found, however, the C value specified
                in the read/write command did not match the C value read from the disc */

				/* no data - checked on Amstrad CPC */
				fdc->upd765_status[1] |= UPD765_ST1_NO_DATA;
				/* bad C value */
				fdc->upd765_status[2] |= UPD765_ST2_WRONG_CYLINDER;

				if (id.C == 0x0ff)
				{
					/* the C value is 0x0ff which indicates a bad track in the IBM soft-sectored
                    format */
					fdc->upd765_status[2] |= UPD765_ST2_BAD_CYLINDER;
				}

				return FALSE;
			}
		}

		 /* index set? */
		if (floppy_drive_get_flag_state(img, FLOPPY_DRIVE_INDEX))
		{
			index_count++;
		}

	}
	while (index_count!=2);

	if (fdc->upd765_command_bytes[4] != fdc->upd765_command_bytes[6])
	{
		/* no data - specified sector ID was not found */
		fdc->upd765_status[1] |= UPD765_ST1_NO_DATA;
	}

	return 0;
}

static void upd765_read_complete(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
/* causes problems!!! - need to fix */
#ifdef NO_END_OF_CYLINDER
	/* set end of cylinder */
	fdc->upd765_status[1] &= ~UPD765_ST1_END_OF_CYLINDER;
#else
	/* completed read command */

	/* end of cylinder is set when:
        - a whole sector has been read
        - terminal count input is not set
        - AND the the sector specified by EOT was read
        */

	/* if end of cylinder is set, and we did receive a terminal count, then clear it */
	if ((fdc->upd765_flags & UPD765_TC)!=0)
	{
		/* set end of cylinder */
		fdc->upd765_status[1] &= ~UPD765_ST1_END_OF_CYLINDER;
	}
#endif

	upd765_setup_st0(device);

	fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
	fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
	fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
	fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
	fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
	fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
	fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */

	upd765_setup_result_phase(device,7);
}

static void upd765_read_data(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	device_t *img = current_image(device);

	if (!upd765_get_rdy(device))
	{
		fdc->upd765_status[0] = 0x0c0 | (1<<4) | fdc->drive | (fdc->side<<2);
		fdc->upd765_status[1] = 0x00;
		fdc->upd765_status[2] = 0x00;

		fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
		fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
		fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
		fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
		fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
		fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
		fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */
		upd765_setup_result_phase(device,7);
		return;
	}

	if (LOG_VERBOSE)
		logerror("sector c: %02x h: %02x r: %02x n: %02x\n",fdc->upd765_command_bytes[2], fdc->upd765_command_bytes[3],fdc->upd765_command_bytes[4], fdc->upd765_command_bytes[5]);

	/* find a sector to read data from */
	{
		int found_sector_to_read;

		found_sector_to_read = 0;
		/* check for finished reading sectors */
		do
		{
			/* get matching sector */
			if (upd765_get_matching_sector(device))
			{

				/* skip it? */
				if (upd765_read_skip_sector(device))
				{
					/* yes */

					/* check that we haven't finished reading all sectors */
					if (upd765_sector_count_complete(device))
					{
						/* read complete */
						upd765_read_complete(device);
						return;
					}

					/* read not finished */

					/* increment sector count */
					upd765_increment_sector(device);
				}
				else
				{
					/* found a sector to read */
					found_sector_to_read = 1;
				}
			}
			else
			{
				/* error in finding sector */
				upd765_read_complete(device);
				return;
			}
		}
		while (found_sector_to_read==0);
	}

	{
		int data_size;

		data_size = upd765_n_to_bytes(fdc->upd765_command_bytes[5]);

		floppy_drive_read_sector_data(img, fdc->side, fdc->sector_id,fdc->data_buffer,data_size);

        upd765_setup_execution_phase_read(device,fdc->data_buffer, data_size);
	}
}


static void upd765_format_track(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	device_t *img = current_image(device);

	/* write protected? */
	if (floppy_wpt_r(img) == CLEAR_LINE)
	{
		fdc->upd765_status[1] |= UPD765_ST1_NOT_WRITEABLE;

		upd765_setup_st0(device);
		/* TODO: Check result is correct */
			fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
            fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
            fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
			fdc->upd765_result_bytes[3] = fdc->format_data[0];
			fdc->upd765_result_bytes[4] = fdc->format_data[1];
			fdc->upd765_result_bytes[5] = fdc->format_data[2];
			fdc->upd765_result_bytes[6] = fdc->format_data[3];
			upd765_setup_result_phase(device,7);

		return;
	}

    upd765_setup_execution_phase_write(device, &fdc->format_data[0], 4);
}

static void upd765_read_a_track(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	int data_size;

	/* SKIP not allowed with this command! */

	/* get next id */
	chrn_id id;

	upd765_get_next_id(device, &id);

	/* TO BE CONFIRMED! */
	/* check id from disc */
	if (id.C==fdc->upd765_command_bytes[2])
	{
		if (id.H==fdc->upd765_command_bytes[3])
		{
			if (id.R==fdc->upd765_command_bytes[4])
			{
				if (id.N==fdc->upd765_command_bytes[5])
				{
					/* if ID found, then no data is not set */
					/* otherwise no data will remain set */
					fdc->upd765_status[1] &=~UPD765_ST1_NO_DATA;
				}
			}
		}
	}


	data_size = upd765_n_to_bytes(id.N);

	floppy_drive_read_sector_data(current_image(device), fdc->side, fdc->sector_id,fdc->data_buffer,data_size);

	upd765_setup_execution_phase_read(device,fdc->data_buffer, data_size);
}

static int upd765_just_read_last_sector_on_track(device_t *device)
{
	if (floppy_drive_get_flag_state(current_image(device), FLOPPY_DRIVE_INDEX))
		return 1;
	return 0;
}

static void upd765_write_complete(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);

/* causes problems!!! - need to fix */
#ifdef NO_END_OF_CYLINDER
        /* set end of cylinder */
        fdc->upd765_status[1] &= ~UPD765_ST1_END_OF_CYLINDER;
#else
	/* completed read command */

	/* end of cylinder is set when:
     - a whole sector has been read
     - terminal count input is not set
     - AND the the sector specified by EOT was read
     */

	/* if end of cylinder is set, and we did receive a terminal count, then clear it */
	if ((fdc->upd765_flags & UPD765_TC)!=0)
	{
		/* set end of cylinder */
		fdc->upd765_status[1] &= ~UPD765_ST1_END_OF_CYLINDER;
	}
#endif

	upd765_setup_st0(device);

    fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
    fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
    fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
    fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
    fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
    fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
    fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */

    upd765_setup_result_phase(device,7);
}


static void upd765_write_data(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	if (!upd765_get_rdy(device))
	{
		fdc->upd765_status[0] = 0x0c0 | (1<<4) | fdc->drive | (fdc->side<<2);
        fdc->upd765_status[1] = 0x00;
        fdc->upd765_status[2] = 0x00;

        fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
        fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
        fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
        fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
        fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
        fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
        fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */
		upd765_setup_result_phase(device,7);
		return;
	}

	/* write protected? */
	if (floppy_wpt_r(current_image(device)) == CLEAR_LINE)
	{
		fdc->upd765_status[1] |= UPD765_ST1_NOT_WRITEABLE;

		upd765_write_complete(device);
		return;
	}

	if (upd765_get_matching_sector(device))
	{
		int data_size;

		data_size = upd765_n_to_bytes(fdc->upd765_command_bytes[5]);

        upd765_setup_execution_phase_write(device,fdc->data_buffer, data_size);
	}
    else
    {
        upd765_setup_result_phase(device,7);
    }
}


/* return true if we have read all sectors, false if not */
static int upd765_sector_count_complete(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
/* this is not correct?? */
#if 1
	/* if terminal count has been set - yes */
	if (fdc->upd765_flags & UPD765_TC)
	{
		/* completed */
		return 1;
	}



	/* multi-track? */
	if (fdc->upd765_command_bytes[0] & 0x080)
	{
		/* it appears that in multi-track mode,
        the EOT parameter of the command is ignored!? -
        or is it ignored the first time and not the next, so that
        if it is started on side 0, it will end at EOT on side 1,
        but if started on side 1 it will end at end of track????

        PC driver requires this to end at last sector on side 1, and
        ignore EOT parameter.

        To be checked!!!!
        */

		/* if just read last sector and on side 1 - finish */
		if (upd765_just_read_last_sector_on_track(device))
		{
			if (floppy_get_heads_per_disk(flopimg_get_image(current_image(device)))==1) {
				return 2;
			} else {
				if (fdc->side==1)
					return 2;
			}
		}

		/* if not on second side then we haven't finished yet */
		if (fdc->side!=1)
		{
			/* haven't finished yet */
			return 0;
		}
	}
	else
	{
		/* sector id == EOT? */
		if (fdc->upd765_command_bytes[4]==fdc->upd765_command_bytes[6])
		{

			/* completed */
			return 2;
		}
	}
#else

	/* if terminal count has been set - yes */
	if (fdc->upd765_flags & UPD765_TC)
	{
		/* completed */
		return 1;
	}

	/* Multi-Track operation:

    Verified on Amstrad CPC.

        disc format used:
            9 sectors per track
            2 sides
            Sector IDs: &01, &02, &03, &04, &05, &06, &07, &08, &09

        Command specified:
            SIDE = 0,
            C = 0,H = 0,R = 1, N = 2, EOT = 1
        Sectors read:
            Sector 1 side 0
            Sector 1 side 1

        Command specified:
            SIDE = 0,
            C = 0,H = 0,R = 1, N = 2, EOT = 3
        Sectors read:
            Sector 1 side 0
            Sector 2 side 0
            Sector 3 side 0
            Sector 1 side 1
            Sector 2 side 1
            Sector 3 side 1


        Command specified:
            SIDE = 0,
            C = 0, H = 0, R = 7, N = 2, EOT = 3
        Sectors read:
            Sector 7 side 0
            Sector 8 side 0
            Sector 9 side 0
            Sector 10 not found. Error "No Data"

        Command specified:
            SIDE = 1,
            C = 0, H = 1, R = 1, N = 2, EOT = 1
        Sectors read:
            Sector 1 side 1

        Command specified:
            SIDE = 1,
            C = 0, H = 1, R = 1, N = 2, EOT = 2
        Sectors read:
            Sector 1 side 1
            Sector 1 side 2

  */

	/* sector id == EOT? */
	if ((fdc->upd765_command_bytes[4]==fdc->upd765_command_bytes[6]))
	{
		/* multi-track? */
		if (fdc->upd765_command_bytes[0] & 0x080)
		{
			/* if we have reached EOT (fdc->upd765_command_bytes[6])
            on side 1, then read is complete */
			if (fdc->side==1)
				return 1;

			return 0;

		}

		/* completed */
		return 1;
	}
#endif
	/* not complete */
	return 0;
}

static void	upd765_increment_sector(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	/* multi-track? */
	if (fdc->upd765_command_bytes[0] & 0x080)
	{
		/* reached EOT? */
		/* if (fdc->upd765_command_bytes[4]==fdc->upd765_command_bytes[6])*/
		if (upd765_just_read_last_sector_on_track(device))
		{
			/* reset sector id to 1 */
			fdc->upd765_command_bytes[4] = 1;

			/* yes */
			if(fdc->side == 1) {
				fdc->upd765_command_bytes[3] = 0;
				fdc->upd765_command_bytes[2]++;
				return;
			}

			/* reached EOT */
			/* change side to 1 */
			fdc->side = 1;
			/* set head to 1 for get next sector test */
			fdc->upd765_command_bytes[3] = 1;
		}
		else
		{
			/* increment */
			fdc->upd765_command_bytes[4]++;
		}

	}
	else
	{
		if (upd765_just_read_last_sector_on_track(device))
		{
			fdc->upd765_command_bytes[4] = 1;
			fdc->upd765_command_bytes[2]++;
			return;
		}

		fdc->upd765_command_bytes[4]++;
	}
}

/* control mark handling code */

/* if SK==0, and we are executing a read data command, and a deleted data sector is found,
the data is not skipped. The data is read, but the control mark is set and the read is stopped */
/* if SK==0, and we are executing a read deleted data command, and a data sector is found,
the data is not skipped. The data is read, but the control mark is set and the read is stopped */
static int upd765_read_data_stop(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	/* skip not set? */
	if ((fdc->upd765_command_bytes[0] & (1<<5))==0)
	{
		/* read data? */
		if (fdc->command == 0x06)
		{
			/* did we just read a sector with deleted data? */
			if (fdc->data_type == UPD765_DAM_DELETED_DATA)
			{
				/* set control mark */
				fdc->upd765_status[2] |= UPD765_ST2_CONTROL_MARK;

				/* quit */
				return TRUE;
			}
		}
		/* deleted data? */
		else
		if (fdc->command == 0x0c)
		{
			/* did we just read a sector with data? */
			if (fdc->data_type == UPD765_DAM_DATA)
			{
				/* set control mark */
				fdc->upd765_status[2] |= UPD765_ST2_CONTROL_MARK;

				/* quit */
				return TRUE;
			}
		}
	}

	/* continue */
	return FALSE;
}

static TIMER_CALLBACK(upd765_continue_command)
{
	device_t *device = (device_t *)ptr;
	upd765_t *fdc = get_safe_token(device);
	if ((fdc->upd765_phase == UPD765_EXECUTION_PHASE_READ) ||
		(fdc->upd765_phase == UPD765_EXECUTION_PHASE_WRITE))
	{
		switch (fdc->command)
        {
			/* read a track */
			case 0x02:
			{
				fdc->sector_counter++;

				/* sector counter == EOT */
				if (fdc->sector_counter==fdc->upd765_command_bytes[6])
				{
					/* TODO: Add correct info here */

					fdc->upd765_status[1] |= UPD765_ST1_END_OF_CYLINDER;

					upd765_setup_st0(device);

					fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
					fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
					fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
					fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
					fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
					fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
					fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */

					upd765_setup_result_phase(device,7);
				}
				else
				{
					upd765_read_a_track(device);
				}
			}
			break;

			/* format track */
			case 0x0d:
			{
				floppy_drive_format_sector(current_image(device), fdc->side, fdc->sector_counter,
					fdc->format_data[0], fdc->format_data[1],
					fdc->format_data[2], fdc->format_data[3],
					fdc->upd765_command_bytes[5]);

				fdc->sector_counter++;

				/* sector_counter = SC */
				if (fdc->sector_counter == fdc->upd765_command_bytes[3])
				{
					/* TODO: Check result is correct */
					fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
					fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
					fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
					fdc->upd765_result_bytes[3] = fdc->format_data[0];
					fdc->upd765_result_bytes[4] = fdc->format_data[1];
					fdc->upd765_result_bytes[5] = fdc->format_data[2];
					fdc->upd765_result_bytes[6] = fdc->format_data[3];
					upd765_setup_result_phase(device,7);
				}
				else
				{
					upd765_format_track(device);
				}
			}
			break;

			/* write data, write deleted data */
			case 0x09:
			case 0x05:
			{
				/* sector id == EOT */
				UINT8 ddam;

				/* nothing to write */
				if ((fdc->upd765_transfer_bytes_remaining==0) && (fdc->upd765_flags & UPD765_DMA_MODE))
				{
					if (fdc->upd765_flags & UPD765_TC)
						upd765_write_complete(device);
					// if TC not set, what?
					break;
				}

				ddam = 0;
				if (fdc->command == 0x09)
				{
					ddam = 1;
				}

				/* write data to disc */
				floppy_drive_write_sector_data(current_image(device), fdc->side, fdc->sector_id,fdc->data_buffer,upd765_n_to_bytes(fdc->upd765_command_bytes[5]),ddam);

				if (upd765_sector_count_complete(device))
				{
					upd765_increment_sector(device);
					upd765_write_complete(device);
				}
				else
				{
					upd765_increment_sector(device);
					upd765_write_data(device);
				}
			}
			break;

			/* read data, read deleted data */
			case 0x0c:
			case 0x06:
			{

				int cause;
				/* read all sectors? */

				/* sector id == EOT */
				if ((cause = upd765_sector_count_complete(device)) || upd765_read_data_stop(device))
				{
					if (cause == 2)
						upd765_increment_sector(device);  // advance to next cylinder if EOT

					upd765_read_complete(device);
				}
				else
				{
					upd765_increment_sector(device);
					upd765_read_data(device);
				}
			}
			break;

			default:
				break;
		}
	}
}


static int upd765_get_command_byte_count(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	fdc->command = fdc->upd765_command_bytes[0] & 0x01f;

	if (fdc->version==TYPE_UPD765A)
	{
		 return upd765_cmd_size[fdc->command];
    }
	else
	{
		if (fdc->version==TYPE_UPD72065)
		{
			switch(fdc->upd765_command_bytes[0])
			{
				case 0x34: // Reset Standby
				case 0x35: // Set Standby
				case 0x36: // Software Reset
					return 1;
				default:
					return upd765_cmd_size[fdc->command];
			}
		}
		if (fdc->version==TYPE_SMC37C78)
		{
			switch (fdc->command)
			{
				/* version */
				case 0x010:
					return 1;

				/* verify */
				case 0x016:
					return 9;

				/* configure */
				case 0x013:
					return 4;

				/* dumpreg */
				case 0x0e:
					return 1;

				/* perpendicular mode */
				case 0x012:
					return 1;

				/* lock */
				case 0x014:
					return 1;

				/* seek/relative seek are together! */

				default:
					return upd765_cmd_size[fdc->command];
			}
		}
	}

	return upd765_cmd_size[fdc->command];
}





void upd765_update_state(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);
	switch (fdc->upd765_phase) {
	case UPD765_RESULT_PHASE:
		/* set data reg */
		fdc->upd765_data_reg = fdc->upd765_result_bytes[fdc->upd765_transfer_bytes_count];

		if (fdc->upd765_transfer_bytes_count == 0)
		{
			/* clear int for specific commands */
			switch (fdc->command) {
			case 2:	/* read a track */
			case 5:	/* write data */
			case 6:	/* read data */
			case 9:	/* write deleted data */
			case 10:	/* read id */
			case 12:	/* read deleted data */
			case 13:	/* format at track */
			case 17:	/* scan equal */
			case 19:	/* scan low or equal */
			case 29:	/* scan high or equal */
				upd765_set_int(device, 0);
				break;

			default:
				break;
			}
		}

		if (LOG_VERBOSE)
			logerror("UPD765: RESULT: %02x\n", fdc->upd765_data_reg);

		fdc->upd765_transfer_bytes_count++;
		fdc->upd765_transfer_bytes_remaining--;

		if (fdc->upd765_transfer_bytes_remaining==0)
		{
			upd765_idle(device);
		}
		else
		{
			upd765_set_data_request(device);
		}
		break;

	case UPD765_EXECUTION_PHASE_READ:
		/* setup data register */
		fdc->upd765_data_reg = fdc->execution_phase_data[fdc->upd765_transfer_bytes_count];
		fdc->upd765_transfer_bytes_count++;
		fdc->upd765_transfer_bytes_remaining--;

		if (LOG_EXTRA)
			logerror("EXECUTION PHASE READ: %02x\n", fdc->upd765_data_reg);

		if ((fdc->upd765_transfer_bytes_remaining==0) || (fdc->upd765_flags & UPD765_TC))
		{
			fdc->command_timer->adjust(attotime::zero);
		}
		else
		{
			/* trigger int */
			upd765_setup_timed_data_request(device,1);
		}
		break;

	case UPD765_COMMAND_PHASE_FIRST_BYTE:
		fdc->FDC_main |= 0x10;                      /* set BUSY */

		if (LOG_VERBOSE)
			logerror("%s: upd765(): command=0x%02x\n", device->machine().describe_context(), fdc->upd765_data_reg);

		/* seek in progress? */
		if (fdc->upd765_flags & UPD765_SEEK_ACTIVE)
		{
			/* any command results in a invalid - I think that seek, recalibrate and
            sense interrupt status may work*/
			if (fdc->upd765_data_reg != 8)	/* Sense Interrupt Status */
				fdc->upd765_data_reg = 0;
		}

		fdc->upd765_command_bytes[0] = fdc->upd765_data_reg;

		fdc->upd765_transfer_bytes_remaining = upd765_get_command_byte_count(device);

		fdc->upd765_transfer_bytes_count = 1;
		fdc->upd765_transfer_bytes_remaining--;

		if (fdc->upd765_transfer_bytes_remaining==0)
		{
			upd765_setup_command(device);
		}
		else
		{
			/* request more data */
			upd765_set_data_request(device);
			fdc->upd765_phase = UPD765_COMMAND_PHASE_BYTES;
		}
        break;

    case UPD765_COMMAND_PHASE_BYTES:
		if (LOG_VERBOSE)
			logerror("%s: upd765(): command=0x%02x\n", device->machine().describe_context(), fdc->upd765_data_reg);

		fdc->upd765_command_bytes[fdc->upd765_transfer_bytes_count] = fdc->upd765_data_reg;
		fdc->upd765_transfer_bytes_count++;
		fdc->upd765_transfer_bytes_remaining--;

		if (fdc->upd765_transfer_bytes_remaining==0)
		{
			upd765_setup_command(device);
		}
		else
		{
			/* request more data */
			upd765_set_data_request(device);
		}
		break;

    case UPD765_EXECUTION_PHASE_WRITE:
		fdc->execution_phase_data[fdc->upd765_transfer_bytes_count]=fdc->upd765_data_reg;
		fdc->upd765_transfer_bytes_count++;
		fdc->upd765_transfer_bytes_remaining--;

		if ((fdc->upd765_transfer_bytes_remaining == 0) || (fdc->upd765_flags & UPD765_TC))
		{
			fdc->command_timer->adjust(attotime::zero);
		}
		else
		{
			upd765_setup_timed_data_request(device,1);
		}
		break;
	}
}


READ8_DEVICE_HANDLER(upd765_data_r)
{
	upd765_t *fdc = get_safe_token(device);

//  if ((fdc->FDC_main & 0x0c0) == 0x0c0)
	if ((fdc->FDC_main & 0x080) == 0x080)
	{
		if (
			(fdc->upd765_phase == UPD765_EXECUTION_PHASE_READ) ||
			(fdc->upd765_phase == UPD765_EXECUTION_PHASE_WRITE))
		{

			/* reading the data byte clears the interrupt */
			upd765_set_int(device,CLEAR_LINE);
		}

		/* reset data request */
		upd765_clear_data_request(device);

		/* update state */
		upd765_update_state(device);
	}

	if (LOG_EXTRA)
		logerror("DATA R: %02x\n", fdc->upd765_data_reg);

	return fdc->upd765_data_reg;
}

WRITE8_DEVICE_HANDLER(upd765_data_w)
{
	upd765_t *fdc = get_safe_token(device);

	if (LOG_EXTRA)
		logerror("DATA W: %02x\n", data);

	/* write data to data reg */
	fdc->upd765_data_reg = data;

	if ((fdc->FDC_main & 0x0c0)==0x080)
	{
		if (
			(fdc->upd765_phase == UPD765_EXECUTION_PHASE_READ) ||
			(fdc->upd765_phase == UPD765_EXECUTION_PHASE_WRITE))
		{

			/* reading the data byte clears the interrupt */
			upd765_set_int(device, CLEAR_LINE);
		}

		/* reset data request */
		upd765_clear_data_request(device);

		/* update state */
		upd765_update_state(device);
	}
}

static void upd765_setup_invalid(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);

	fdc->command = 0;
	fdc->upd765_result_bytes[0] = 0x080;
	upd765_setup_result_phase(device,1);
}

static void upd765_setup_command(device_t *device)
{
	upd765_t *fdc = get_safe_token(device);

	static const char *const commands[] =
	{
		NULL,						/* [00] */
		NULL,						/* [01] */
		"Read Track",				/* [02] */
		"Specify",					/* [03] */
		"Sense Drive Status",		/* [04] */
		"Write Data",				/* [05] */
		"Read Data",				/* [06] */
		"Recalibrate",				/* [07] */
		"Sense Interrupt Status",	/* [08] */
		"Write Deleted Data",		/* [09] */
		"Read ID",					/* [0A] */
		NULL,						/* [0B] */
		"Read Deleted Data",		/* [0C] */
		"Format Track",				/* [0D] */
		"Dump Registers",			/* [0E] */
		"Seek",						/* [0F] */
		"Version",					/* [10] */
		NULL,						/* [11] */
		"Perpendicular Mode",		/* [12] */
		"Configure",				/* [13] */
		"Lock"						/* [14] */
	};

	device_t *img;
	const char *cmd = NULL;
	chrn_id id;

	/* if not in dma mode set execution phase bit */
	if (!(fdc->upd765_flags & UPD765_DMA_MODE))
	{
        fdc->FDC_main |= 0x020;              /* execution phase */
	}

	if (LOG_COMMAND)
	{
		if ((fdc->upd765_command_bytes[0] & 0x1f) < ARRAY_LENGTH(commands))
			cmd = commands[fdc->upd765_command_bytes[0] & 0x1f];
		logerror("upd765_setup_command(): Setting up command 0x%02X (%s)\n",
			fdc->upd765_command_bytes[0] & 0x1f, cmd ? cmd : "???");
	}

	switch (fdc->upd765_command_bytes[0] & 0x1f)
	{
		case 0x03:      /* specify */
			/* setup step rate */
			fdc->srt_in_ms = 16-((fdc->upd765_command_bytes[1]>>4) & 0x0f);

			fdc->upd765_flags &= ~UPD765_DMA_MODE;

			if ((fdc->upd765_command_bytes[2] & 0x01)==0)
			{
				fdc->upd765_flags |= UPD765_DMA_MODE;
			}

			upd765_idle(device);
			break;

		case 0x04:  /* sense drive status */
			upd765_setup_drive_and_side(device);
			img = current_image(device);

			fdc->upd765_status[3] = fdc->drive | (fdc->side<<2);

			if (img)
			{
				fdc->upd765_status[3] |= !floppy_tk00_r(img) << 4;
				fdc->upd765_status[3] |= !floppy_wpt_r(img) << 6;

				if (upd765_get_rdy(device))
				{
					fdc->upd765_status[3] |= 0x20;
				}
			}

			fdc->upd765_status[3] |= 0x08;

			/* two side and fault not set but should be? */
			fdc->upd765_result_bytes[0] = fdc->upd765_status[3];
			upd765_setup_result_phase(device,1);
			break;

		case 0x07:          /* recalibrate */
			upd765_seek_setup(device, 1);
			break;

		case 0x0f:          /* seek */
			upd765_seek_setup(device, 0);
			break;

		case 0x0a:      /* read id */
			upd765_setup_drive_and_side(device);
			img = current_image(device);

			fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
			fdc->upd765_status[1] = 0;
			fdc->upd765_status[2] = 0;

			/* drive ready? */
			if (upd765_get_rdy(device))
			{
				/* is disk inserted? */
				device_image_interface *image = dynamic_cast<device_image_interface *>( img);
				if (image!=NULL && image->exists())
				{
					int index_count = 0;

					/* floppy drive is ready and disc is inserted */

					/* this is the id that appears when a disc is not formatted */
					/* to be checked on Amstrad */
					id.C = 0;
					id.H = 0;
					id.R = 0x01;
					id.N = 0x02;

					/* repeat for two index counts before quitting */
					do
					{
						/* get next id from disc */
						if (floppy_drive_get_next_id(img, fdc->side, &id))
						{
							/* got an id */
							/* if bad media keep going until failure */
							if (!(fdc->upd765_flags & UPD765_BAD_MEDIA)) break;
						}

						if (floppy_drive_get_flag_state(img, FLOPPY_DRIVE_INDEX))
						{
							/* update index count */
							index_count++;
						}
					}
					while (index_count!=2);

					if (fdc->upd765_flags & UPD765_BAD_MEDIA)
					{
							fdc->upd765_status[0] |= 0x40;
							fdc->upd765_status[1] |= 1;
					}

					/* at this point, we have seen a id or two index pulses have occurred! */
					fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
					fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
					fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
					fdc->upd765_result_bytes[3] = id.C; /* C */
					fdc->upd765_result_bytes[4] = id.H; /* H */
					fdc->upd765_result_bytes[5] = id.R; /* R */
					fdc->upd765_result_bytes[6] = id.N; /* N */

					upd765_setup_result_phase(device,7);
				}
				else
				{
					/* floppy drive is ready, but no disc is inserted */
					/* this occurs on the PC */
					/* in this case, the command never quits! */
					/* there are no index pulses to stop the command! */
				}
			}
			else
			{
				/* what are id values when drive not ready? */

				/* not ready, abnormal termination */
				fdc->upd765_status[0] |= (1<<3) | (1<<6);
				fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
				fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
				fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
				fdc->upd765_result_bytes[3] = 0; /* C */
				fdc->upd765_result_bytes[4] = 0; /* H */
				fdc->upd765_result_bytes[5] = 0; /* R */
				fdc->upd765_result_bytes[6] = 0; /* N */
			}
			break;


		case 0x08: /* sense interrupt status */
			/* interrupt pending? */
			if (fdc->upd765_flags & UPD765_INT)
			{
				/* yes. Clear int */
				upd765_set_int(device, CLEAR_LINE);

				/* clear drive seek bits */
				fdc->FDC_main &= ~(1 | 2 | 4 | 8);

				/* return status */
				fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
				/* return pcn */
				fdc->upd765_result_bytes[1] = fdc->pcn[fdc->drive];

				/* return result */
				upd765_setup_result_phase(device,2);
			}
			else
			{
				if(fdc->version == TYPE_UPD72065 && (fdc->FDC_main & 0x0f) == 0x00)
				{  // based on XM6
					upd765_setup_invalid(device);
				}
				else
				{
					/* no int */
					fdc->upd765_result_bytes[0] = 0x80;
					/* return pcn */
					fdc->upd765_result_bytes[1] = fdc->pcn[fdc->drive];

					/* return result */
					upd765_setup_result_phase(device,2);
				}
			}
			break;

		case 0x06:  /* read data */
			upd765_setup_drive_and_side(device);

			fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
			fdc->upd765_status[1] = 0;
			fdc->upd765_status[2] = 0;

			upd765_read_data(device);
			break;

		case 0x0c:
			/* read deleted data */
			upd765_setup_drive_and_side(device);

			fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
			fdc->upd765_status[1] = 0;
			fdc->upd765_status[2] = 0;

			/* .. for now */
			upd765_read_data(device);
			break;

		case 0x09:
			/* write deleted data */
			upd765_setup_drive_and_side(device);

			fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
			fdc->upd765_status[1] = 0;
			fdc->upd765_status[2] = 0;

			/* ... for now */
			upd765_write_data(device);
			break;

		case 0x02:
			/* read a track */
			upd765_setup_drive_and_side(device);
			img = current_image(device);
			fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
			fdc->upd765_status[1] = 0;
			fdc->upd765_status[2] = 0;

			fdc->upd765_status[0] |= UPD765_ST1_NO_DATA;

			/* wait for index */
			do
			{
				/* get next id from disc */
				floppy_drive_get_next_id(img, fdc->side,&id);
			}
			while ((floppy_drive_get_flag_state(img, FLOPPY_DRIVE_INDEX))==0);

			fdc->sector_counter = 0;

			upd765_read_a_track(device);
			break;

		case 0x05:  /* write data */
			upd765_setup_drive_and_side(device);

			fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
			fdc->upd765_status[1] = 0;
			fdc->upd765_status[2] = 0;

			upd765_write_data(device);
			break;

		case 0x0d:	/* format a track */
			upd765_setup_drive_and_side(device);

			fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
			fdc->upd765_status[1] = 0;
			fdc->upd765_status[2] = 0;

			fdc->sector_counter = 0;

			upd765_format_track(device);
			break;

		default:	/* invalid */
			switch (fdc->version)
			{
				case TYPE_UPD765A:
					upd765_setup_invalid(device);
					break;

				case TYPE_UPD765B:
					/* from upd765b data sheet */
					if ((fdc->upd765_command_bytes[0] & 0x01f)==0x010)
					{
						/* version */
						fdc->upd765_status[0] = 0x090;
						fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
						upd765_setup_result_phase(device,1);
					}
					break;

				case TYPE_UPD72065:
					switch(fdc->upd765_command_bytes[0] & 0x3f)
					{
						case 0x36:  // Software Reset
							upd765_reset(device,0);
							upd765_idle(device);
							if(LOG_COMMAND)
								logerror("upd72065: command - Software Reset\n");
							break;
						default:
							upd765_setup_invalid(device);
							break;
					}
					break;

				case TYPE_SMC37C78:
					/* TO BE COMPLETED!!! !*/
					switch (fdc->upd765_command_bytes[0] & 0x1f)
					{
						case 0x10:		/* version */
							fdc->upd765_status[0] = 0x90;
							fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
							upd765_setup_result_phase(device,1);
							break;

						case 0x13:		/* configure */
							upd765_idle(device);
							break;

						case 0x0e:		/* dump reg */
							fdc->upd765_result_bytes[0] = fdc->pcn[0];
							fdc->upd765_result_bytes[1] = fdc->pcn[1];
							fdc->upd765_result_bytes[2] = fdc->pcn[2];
							fdc->upd765_result_bytes[3] = fdc->pcn[3];

							upd765_setup_result_phase(device,10);
							break;

						case 0x12:		/* perpendicular mode */
							upd765_idle(device);
							break;

						case 0x14:		/* lock */
							upd765_setup_result_phase(device,1);
							break;
					}
					break;
			}
	}
}


/* dma acknowledge write */
WRITE8_DEVICE_HANDLER(upd765_dack_w)
{
	/* clear request */
	upd765_set_dma_drq(device, CLEAR_LINE);

	/* write data */
	upd765_data_w(device, offset, data);
}

READ8_DEVICE_HANDLER(upd765_dack_r)
{
	/* clear data request */
	upd765_set_dma_drq(device,CLEAR_LINE);

	/* read data */
	return upd765_data_r(device, offset);
}

static TIMER_CALLBACK( interrupt_callback )
{
	device_t* device = (device_t*)ptr;
	upd765_set_int(device, 1);
}

void upd765_set_bad(device_t *device, int state)
{
	upd765_t *fdc = get_safe_token(device);
	if (state) fdc->upd765_flags |= UPD765_BAD_MEDIA;
	else fdc->upd765_flags &= ~UPD765_BAD_MEDIA;
}

void upd765_reset(device_t *device, int offset)
{
	upd765_t *fdc = get_safe_token(device);

	/* upd765 in idle state - ready to accept commands */
	upd765_idle(device);

	/* set int low */
	upd765_set_int(device, 0);
	/* set dma drq output */
	upd765_set_dma_drq(device,0);

	/* tandy 100hx assumes that after NEC is reset, it is in DMA mode */
	fdc->upd765_flags |= UPD765_DMA_MODE;

	/* if ready input is set during reset generate an int */
	if (upd765_get_rdy(device))
	{
		int i;
		int a_drive_is_ready;

		fdc->upd765_status[0] = 0x080 | 0x040;

		/* for the purpose of pc-xt. If any of the drives have a disk inserted,
        do not set not-ready - need to check with pc_fdc.c whether all drives
        are checked or only the drive selected with the drive select bits?? */

		a_drive_is_ready = 0;
		for (i = 0; i < 4; i++)
		{
			if (fdc->intf->floppy_drive_tags[i]!=NULL)
			{
				device_t *img;

				if (device->owner() != NULL)
					img = device->owner()->subdevice(fdc->intf->floppy_drive_tags[i]);
				else
					img = device->machine().device(fdc->intf->floppy_drive_tags[i]);

				device_image_interface *image = dynamic_cast<device_image_interface *>( img);
				if (image->exists())
				{
					a_drive_is_ready = 1;
					break;
				}
			}

		}

		if (!a_drive_is_ready && fdc->intf->rdy_pin == UPD765_RDY_PIN_CONNECTED )
		{
			fdc->upd765_status[0] |= 0x08;
		}

		device->machine().scheduler().timer_set(attotime::from_usec(5), FUNC(interrupt_callback),0,device);
	}
}

WRITE_LINE_DEVICE_HANDLER( upd765_reset_w )
{
	upd765_t *fdc = get_safe_token(device);

	int flags;

	/* get previous reset state */
	flags = fdc->upd765_flags;

	/* set new reset state */
	/* clear reset */
	fdc->upd765_flags &= ~UPD765_RESET;

	/* reset */
	if (state)
	{
		fdc->upd765_flags |= UPD765_RESET;

		upd765_set_int(device, 0);
	}

	/* reset changed state? */
	if (((flags^fdc->upd765_flags) & UPD765_RESET)!=0)
	{
		/* yes */

		/* no longer reset */
		if ((fdc->upd765_flags & UPD765_RESET)==0)
		{
			/* reset nec */
			upd765_reset(device, 0);
		}
	}
}


WRITE_LINE_DEVICE_HANDLER( upd765_ready_w )
{
	upd765_t *fdc = get_safe_token(device);

	/* clear ready state */
	fdc->upd765_flags &= ~UPD765_FDD_READY;

	if (state)
	{
		fdc->upd765_flags |= UPD765_FDD_READY;
	}
}


/* Device Interface */

static void common_start(device_t *device, int device_type)
{
	upd765_t *fdc = get_safe_token(device);
	// validate arguments

	assert(device != NULL);
	assert(device->tag() != NULL);
	assert(device->static_config() != NULL);

	fdc->intf = (const upd765_interface*)device->static_config();

	fdc->version = (UPD765_VERSION)device_type;
	fdc->timer = device->machine().scheduler().timer_alloc(FUNC(upd765_timer_callback), (void*)device);
	fdc->seek_timer = device->machine().scheduler().timer_alloc(FUNC(upd765_seek_timer_callback), (void*)device);
	fdc->command_timer = device->machine().scheduler().timer_alloc(FUNC(upd765_continue_command), (void*)device);

	fdc->upd765_flags &= UPD765_FDD_READY;
	fdc->data_buffer = auto_alloc_array(device->machine(), char, 32*1024);

	fdc->out_int_func.resolve(fdc->intf->out_int_func, *device);
	fdc->out_drq_func.resolve(fdc->intf->out_drq_func, *device);

	// register for state saving
	//state_save_register_item(device->machine(), "upd765", device->tag(), 0, upd765->number);
}

static DEVICE_START( upd765a )
{
	common_start(device, TYPE_UPD765A);
}

static DEVICE_START( upd765b )
{
	common_start(device, TYPE_UPD765B);
}

static DEVICE_START( smc37c78 )
{
	common_start(device, TYPE_SMC37C78);
}

static DEVICE_START( upd72065 )
{
	common_start(device, TYPE_UPD72065);
}

static DEVICE_RESET( upd765 )
{
	int i;
	upd765_t *fdc = get_safe_token(device);
	for (i = 0; i < 4; i++) {
		if (fdc->intf->floppy_drive_tags[i]!=NULL)
		{
			device_t *img;

			if (device->owner() != NULL)
				img = device->owner()->subdevice(fdc->intf->floppy_drive_tags[i]);
			else
				img = device->machine().device(fdc->intf->floppy_drive_tags[i]);

			floppy_drive_set_controller(img, device);
			floppy_drive_set_ready_state_change_callback(img, upd765_set_ready_change_callback);
		}
	}

	upd765_reset(device,0);
}

DEVICE_GET_INFO( upd765a )
{
	switch (state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case DEVINFO_INT_TOKEN_BYTES:					info->i = sizeof(upd765_t);					break;
		case DEVINFO_INT_INLINE_CONFIG_BYTES:			info->i = 0;								break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_FCT_START:							info->start = DEVICE_START_NAME(upd765a);	break;
		case DEVINFO_FCT_STOP:							/* Nothing */								break;
		case DEVINFO_FCT_RESET:							info->reset = DEVICE_RESET_NAME(upd765);	break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:							strcpy(info->s, "UPD765A");						break;
		case DEVINFO_STR_FAMILY:						strcpy(info->s, "UPD765A");						break;
		case DEVINFO_STR_VERSION:						strcpy(info->s, "1.0");							break;
		case DEVINFO_STR_SOURCE_FILE:					strcpy(info->s, __FILE__);							break;
		case DEVINFO_STR_CREDITS:						strcpy(info->s, "Copyright MESS Team");			break;
	}
}


DEVICE_GET_INFO( upd765b )
{
	switch (state)
	{
		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:							strcpy(info->s, "UPD765B");				break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_FCT_START:							info->start = DEVICE_START_NAME(upd765b);	break;

		default:										DEVICE_GET_INFO_CALL(upd765a);				break;
	}
}

DEVICE_GET_INFO( smc37c78 )
{
	switch (state)
	{
		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:							strcpy(info->s, "SMC37C78");				break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_FCT_START:							info->start = DEVICE_START_NAME(smc37c78);	break;

		default:										DEVICE_GET_INFO_CALL(upd765a);				break;
	}
}

DEVICE_GET_INFO( upd72065 )
{
	switch (state)
	{
		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:							strcpy(info->s, "UPD72065");				break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_FCT_START:							info->start = DEVICE_START_NAME(upd72065);	break;

		default:										DEVICE_GET_INFO_CALL(upd765a);				break;
	}
}

DEFINE_LEGACY_DEVICE(UPD765A, upd765a);
DEFINE_LEGACY_DEVICE(UPD765B, upd765b);
DEFINE_LEGACY_DEVICE(SMC37C78, smc37c78);
DEFINE_LEGACY_DEVICE(UPD72065, upd72065);
