summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/machine/upd765.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/machine/upd765.c')
-rw-r--r--src/mess/machine/upd765.c2547
1 files changed, 2547 insertions, 0 deletions
diff --git a/src/mess/machine/upd765.c b/src/mess/machine/upd765.c
new file mode 100644
index 00000000000..8b1fc92f97f
--- /dev/null
+++ b/src/mess/machine/upd765.c
@@ -0,0 +1,2547 @@
+/***************************************************************************
+
+ 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_MT 0x80
+
+#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;
+
+ UINT8 ready_changed;
+
+ emu_timer *seek_timer;
+ emu_timer *timer;
+ int timer_type;
+
+ emu_timer *command_timer;
+
+ char *data_buffer;
+ const upd765_interface *intf;
+
+ bool pool;
+};
+
+//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);
+}
+
+READ_LINE_DEVICE_HANDLER( upd765_int_r )
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ return (fdc->upd765_flags & UPD765_INT) ? 1 : 0;
+}
+
+READ_LINE_DEVICE_HANDLER( upd765_drq_r )
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ return (fdc->upd765_flags & UPD765_DMA_DRQ) ? 1 : 0;
+}
+
+
+/* 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] & UPD765_MT)
+ {
+ /* 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;
+ return 1; // do not advance to next cylinder
+ }
+ }
+
+ /* 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] & UPD765_MT)
+ {
+ /* reached EOT? */
+ if (fdc->upd765_command_bytes[4] == fdc->upd765_command_bytes[6])
+ {
+ if (fdc->side == 1)
+ {
+ fdc->upd765_command_bytes[2]++;
+ }
+
+ fdc->upd765_command_bytes[3] ^= 0x01;
+ fdc->upd765_command_bytes[4] = 1;
+ fdc->side = fdc->upd765_command_bytes[3] & 0x01;
+ }
+ else
+ {
+ fdc->upd765_command_bytes[4]++;
+ }
+ }
+ else
+ {
+ if (fdc->upd765_command_bytes[4] == fdc->upd765_command_bytes[6])
+ {
+ fdc->upd765_command_bytes[2]++;
+ fdc->upd765_command_bytes[4] = 1;
+ }
+ else
+ {
+ 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;
+
+ ddam = 0;
+ if (fdc->command == 0x09)
+ {
+ ddam = 1;
+ }
+
+ /* write data to disc */
+ // logerror("floppy_drive_write_sector_data side=%d sector=%d tc=%x\n", fdc->side, fdc->sector_id, fdc->upd765_flags & UPD765_TC);
+ 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);
+
+ /* 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);
+ break;
+ }
+ }
+
+ 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)
+ {
+ /* clear ready changed bit */
+ fdc->ready_changed &= ~(1 << fdc->drive);
+
+ if (!fdc->pool) {
+ fdc->ready_changed = 0;
+ }
+
+ /* 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);
+
+ if (fdc->ready_changed)
+ {
+ fdc->drive++;
+ fdc->upd765_status[0] = 0xc0 | fdc->drive;
+ }
+ else
+ {
+ /* Clear int */
+ upd765_set_int(device, CLEAR_LINE);
+ }
+ }
+ 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 */
+ fdc->pool = fdc->upd765_command_bytes[1] & 0x10;
+ 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;
+
+ // HACK signal ready changed for all drives
+ fdc->ready_changed = 0x0f;
+ fdc->drive = 0;
+
+ /* 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);
+
+ // plain upd765 is doing pooling
+ fdc->pool = true;
+ // 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);
+ // specified in documentation that by default is off
+ upd765_t *fdc = get_safe_token(device);
+ fdc->pool = false;
+}
+
+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);