summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/wd17xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/wd17xx.c')
-rw-r--r--trunk/src/emu/machine/wd17xx.c2264
1 files changed, 2264 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/wd17xx.c b/trunk/src/emu/machine/wd17xx.c
new file mode 100644
index 00000000000..8403d6e8183
--- /dev/null
+++ b/trunk/src/emu/machine/wd17xx.c
@@ -0,0 +1,2264 @@
+/***************************************************************************
+
+ wd17xx.c
+
+ Implementations of the Western Digital 17xx and 27xx families of
+ floppy disk controllers
+
+
+ Models:
+
+ DAL DD Side Clock Remark
+ ---------------------------------------------------------
+ FD1771 1 or 2 MHz First model
+ FD1781 x 1 or 2 MHz
+ FD1791 x 1 or 2 MHz
+ FD1792 1 or 2 MHz
+ FD1793 x x 1 or 2 MHz
+ FD1794 x 1 or 2 MHz
+ FD1795 x x 1 or 2 MHz
+ FD1797 x x x 1 or 2 MHz
+ FD1761 x 1 MHz
+ FD1762 1 MHz ?
+ FD1763 x x 1 MHz
+ FD1764 x 1 MHz ?
+ FD1765 x x 1 MHz
+ FD1767 x x x 1 MHz
+ WD2791 x 1 or 2 MHz Internal data separator
+ WD2793 x x 1 or 2 MHz Internal data separator
+ WD2795 x x 1 or 2 MHz Internal data separator
+ WD2797 x x x 1 or 2 MHz Internal data separator
+ WD1770 x x 8 MHz Motor On signal
+ WD1772 x x 8 MHz Motor On signal, Faster stepping rates
+ WD1773 x x 8 MHz Enable precomp line
+
+ Notes: - In general, later models include all features of earlier models
+ - DAL: Data access lines, x = TRUE; otherwise inverted
+ - DD: Double density support
+ - Side: Has side select support
+ - ?: Unknown if it really exists
+
+ Clones:
+
+ - SMC FD179x
+ - Fujitsu MB8876 -> FD1791, MB8877 -> FD1793
+ - VLSI VL177x
+
+
+ Changelog:
+
+ Kevin Thacker
+ - Removed disk image code and replaced it with floppy drive functions.
+ Any disc image is now useable with this code.
+ - Fixed write protect
+
+ 2005-Apr-16 P.Harvey-Smith:
+ - Increased the delay in wd17xx_timed_read_sector_request and
+ wd17xx_timed_write_sector_request, to 40us to more closely match
+ what happens on the real hardware, this has fixed the NitrOS9 boot
+ problems.
+
+ 2007-Nov-01 Wilbert Pol:
+ Needed these changes to get the MB8877 for Osborne-1 to work:
+ - Added support for multiple record read
+ - Changed the wd17xx_read_id to not return after DATADONEDELAY, but
+ the host should read the id data through the data register. This
+ was accomplished by making this change in the wd17xx_read_id
+ function:
+ - wd17xx_complete_command(device, DELAY_DATADONE);
+ + wd17xx_set_data_request();
+
+ 2009-May-10 Robbbert:
+ Further change to get the Kaypro II to work
+ - When wd17xx_read_id has generated the 6 data bytes, it should make
+ an IRQ and turn off the busy status. The timing for Osborne is
+ critical, it must be between 300 and 700 usec, I've settled on 400.
+ The Kaypro doesn't care timewise, but the busy flag must turn off
+ sometime or it hangs.
+ - w->status |= STA_2_BUSY;
+ + wd17xx_set_busy(device, attotime::from_usec(400));
+
+ 2009-June-4 Roberto Lavarone:
+ - Initial support for wd1771
+ - Added simulation of head loaded feedback from drive
+ - Bugfix: busy flag was cleared too early
+
+ 2009-June-21 Robbbert:
+ - The Bugfix above, while valid, caused the Osborne1 to fail. This
+ is because the delay must not exceed 14usec (found by extensive testing).
+ - The minimum delay is 1usec, need by z80netf while formatting a disk.
+ - http://www.bannister.org/forums/ubbthreads.php?ubb=showflat&Number=50889#Post50889
+ explains the problems, testing done, and the test procedure for the z80netf.
+ - Thus, the delay is set to 10usec, and all the disks I have (not many)
+ seem to work.
+ - Note to anyone who wants to change something: Make sure that the
+ Osborne1 boots up! It is extremely sensitive to timing!
+ - For testing only: The osborne1 rom can be patched to make it much
+ more stable, by changing the byte at 0x0da7 from 0x28 to 0x18.
+
+ 2009-June-25 Robbbert:
+ - Turns out kayproii not working, 10usec is too short.. but 11usec is ok.
+ Setting it to 12usec.
+ Really, this whole thing needs a complete rewrite.
+
+ 2009-July-08 Roberto Lavarone:
+ - Fixed a bug in head load flag handling: einstein and samcoupe now working again
+
+ 2009-September-30 Robbbert:
+ - Modified what status flags are returned after a Forced Interrupt,
+ to allow Microbee to boot CP/M.
+
+ 2010-Jan-31 Phill Harvey-Smith
+ - The above bugfixes for the Kaypro/Osborne1 have borked the booting on the Dragon
+ Alpha. The Alpha it seems needs a delay of ay least 17us or the NMI generated by
+ INTRQ happens too early and doen't break out of the read/write bytes loops.
+
+ I have made the delay settable by calling wd17xx_set_complete_command_delay, and
+ let it default to 12us, as required above, so that the Dragon Alpha works again.
+ This hopefully should not break the other machines.
+
+ This should probably be considdered a minor hack but it does seem to work !
+
+ 2010-02-04 Phill Harvey-Smith
+ - Added multiple sector write as the RM Nimbus needs it.
+
+ 2010-March-22 Curt Coder:
+ - Implemented immediate and index pulse interrupts.
+
+ 2010-Dec-31 Phill Harvey-Smith
+ - Copied multi-sector write code from r7263, for some reason this had been
+ silently removed, but is required for the rmnimbus driver.
+
+ 2011-Mar-08 Phill Harvey-Smith
+ - Triggering intrq now clears the DRQ bit in the status as well as the busy bit.
+ Execution of the READ_DAM command now correctly sets w->command.
+
+ 2011-Apr-01 Curt Coder
+ - Set complete command delay to 16 usec (DD) / 32 usec (SD) and removed
+ the external delay setting hack.
+
+ 2011-Jun-24 Curt Coder
+ - Added device types for all known variants, and enforced inverted DAL lines.
+
+ TODO:
+ - What happens if a track is read that doesn't have any id's on it?
+ (e.g. unformatted disc)
+ - Rewrite into a C++ device
+
+***************************************************************************/
+
+
+#include "emu.h"
+#include "formats/imageutl.h"
+#include "machine/wd17xx.h"
+#include "imagedev/flopdrv.h"
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define VERBOSE 0 /* General logging */
+#define VERBOSE_DATA 0 /* Logging of each byte during read and write */
+
+#define DELAY_ERROR 3
+#define DELAY_NOTREADY 1
+#define DELAY_DATADONE 3
+
+#define TYPE_I 1
+#define TYPE_II 2
+#define TYPE_III 3
+#define TYPE_IV 4
+
+#define FDC_STEP_RATE 0x03 /* Type I additional flags */
+#define FDC_STEP_VERIFY 0x04 /* verify track number */
+#define FDC_STEP_HDLOAD 0x08 /* load head */
+#define FDC_STEP_UPDATE 0x10 /* update track register */
+
+#define FDC_RESTORE 0x00 /* Type I commands */
+#define FDC_SEEK 0x10
+#define FDC_STEP 0x20
+#define FDC_STEP_IN 0x40
+#define FDC_STEP_OUT 0x60
+
+#define FDC_MASK_TYPE_I (FDC_STEP_HDLOAD|FDC_STEP_VERIFY|FDC_STEP_RATE)
+
+/* Type I commands status */
+#define STA_1_BUSY 0x01 /* controller is busy */
+#define STA_1_IPL 0x02 /* index pulse */
+#define STA_1_TRACK0 0x04 /* track 0 detected */
+#define STA_1_CRC_ERR 0x08 /* CRC error */
+#define STA_1_SEEK_ERR 0x10 /* seek error */
+#define STA_1_HD_LOADED 0x20 /* head loaded */
+#define STA_1_WRITE_PRO 0x40 /* floppy is write protected */
+#define STA_1_NOT_READY 0x80 /* drive not ready */
+#define STA_1_MOTOR_ON 0x80 /* status of the Motor On output (WD1770 and WD1772 only) */
+
+/* Type II and III additional flags */
+#define FDC_DELETED_AM 0x01 /* read/write deleted address mark */
+#define FDC_SIDE_CMP_T 0x02 /* side compare track data */
+#define FDC_15MS_DELAY 0x04 /* delay 15ms before command */
+#define FDC_SIDE_CMP_S 0x08 /* side compare sector data */
+#define FDC_MULTI_REC 0x10 /* only for type II commands */
+
+/* Type II commands */
+#define FDC_READ_SEC 0x80 /* read sector */
+#define FDC_WRITE_SEC 0xA0 /* write sector */
+
+#define FDC_MASK_TYPE_II (FDC_MULTI_REC|FDC_SIDE_CMP_S|FDC_15MS_DELAY|FDC_SIDE_CMP_T|FDC_DELETED_AM)
+
+/* Type II commands status */
+#define STA_2_BUSY 0x01
+#define STA_2_DRQ 0x02
+#define STA_2_LOST_DAT 0x04
+#define STA_2_CRC_ERR 0x08
+#define STA_2_REC_N_FND 0x10
+#define STA_2_REC_TYPE 0x20
+#define STA_2_WRITE_PRO 0x40
+#define STA_2_NOT_READY 0x80
+
+#define FDC_MASK_TYPE_III (FDC_SIDE_CMP_S|FDC_15MS_DELAY|FDC_SIDE_CMP_T|FDC_DELETED_AM)
+
+/* Type III commands */
+#define FDC_READ_DAM 0xc0 /* read data address mark */
+#define FDC_READ_TRK 0xe0 /* read track */
+#define FDC_WRITE_TRK 0xf0 /* write track (format) */
+
+/* Type IV additional flags */
+#define FDC_IM0 0x01 /* interrupt mode 0 */
+#define FDC_IM1 0x02 /* interrupt mode 1 */
+#define FDC_IM2 0x04 /* interrupt mode 2 */
+#define FDC_IM3 0x08 /* interrupt mode 3 */
+
+#define FDC_MASK_TYPE_IV (FDC_IM3|FDC_IM2|FDC_IM1|FDC_IM0)
+
+/* Type IV commands */
+#define FDC_FORCE_INT 0xd0 /* force interrupt */
+
+/* structure describing a double density track */
+#define TRKSIZE_DD 6144
+#if 0
+static const UINT8 track_DD[][2] = {
+ {16, 0x4e}, /* 16 * 4E (track lead in) */
+ { 8, 0x00}, /* 8 * 00 (pre DAM) */
+ { 3, 0xf5}, /* 3 * F5 (clear CRC) */
+
+ { 1, 0xfe}, /* *** sector *** FE (DAM) */
+ { 1, 0x80}, /* 4 bytes track,head,sector,seclen */
+ { 1, 0xf7}, /* 1 * F7 (CRC) */
+ {22, 0x4e}, /* 22 * 4E (sector lead in) */
+ {12, 0x00}, /* 12 * 00 (pre AM) */
+ { 3, 0xf5}, /* 3 * F5 (clear CRC) */
+ { 1, 0xfb}, /* 1 * FB (AM) */
+ { 1, 0x81}, /* x bytes sector data */
+ { 1, 0xf7}, /* 1 * F7 (CRC) */
+ {16, 0x4e}, /* 16 * 4E (sector lead out) */
+ { 8, 0x00}, /* 8 * 00 (post sector) */
+ { 0, 0x00}, /* end of data */
+};
+#endif
+
+/* structure describing a single density track */
+#define TRKSIZE_SD 3172
+#if 0
+static const UINT8 track_SD[][2] = {
+ {16, 0xff}, /* 16 * FF (track lead in) */
+ { 8, 0x00}, /* 8 * 00 (pre DAM) */
+ { 1, 0xfc}, /* 1 * FC (clear CRC) */
+
+ {11, 0xff}, /* *** sector *** 11 * FF */
+ { 6, 0x00}, /* 6 * 00 (pre DAM) */
+ { 1, 0xfe}, /* 1 * FE (DAM) */
+ { 1, 0x80}, /* 4 bytes track,head,sector,seclen */
+ { 1, 0xf7}, /* 1 * F7 (CRC) */
+ {10, 0xff}, /* 10 * FF (sector lead in) */
+ { 4, 0x00}, /* 4 * 00 (pre AM) */
+ { 1, 0xfb}, /* 1 * FB (AM) */
+ { 1, 0x81}, /* x bytes sector data */
+ { 1, 0xf7}, /* 1 * F7 (CRC) */
+ { 0, 0x00}, /* end of data */
+};
+#endif
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _wd1770_state wd1770_state;
+struct _wd1770_state
+{
+ /* callbacks */
+ devcb_resolved_read_line in_dden_func;
+ devcb_resolved_write_line out_intrq_func;
+ devcb_resolved_write_line out_drq_func;
+
+ /* input lines */
+ int mr; /* master reset */
+ int rdy; /* ready, enable precomp */
+ int tr00; /* track 00 */
+ int idx; /* index */
+ int wprt; /* write protect */
+ int dden; /* double density */
+
+ /* output lines */
+ int mo; /* motor on */
+ int dirc; /* direction */
+ int drq; /* data request */
+ int intrq; /* interrupt request */
+
+ /* register */
+ UINT8 data_shift;
+ UINT8 data;
+ UINT8 track;
+ UINT8 sector;
+ UINT8 command;
+ UINT8 status;
+ UINT8 interrupt;
+
+ int stepping_rate[4]; /* track stepping rate in ms */
+
+ unsigned short crc; /* Holds the current CRC value for write_track CRC calculation */
+ int crc_active; /* Flag indicating that CRC calculation in write_track is active. */
+
+ UINT8 track_reg; /* value of track register */
+ UINT8 command_type; /* last command type */
+ UINT8 head; /* current head # */
+
+ UINT8 read_cmd; /* last read command issued */
+ UINT8 write_cmd; /* last write command issued */
+ INT8 direction; /* last step direction */
+ UINT8 last_command_data; /* last command data */
+
+ UINT8 status_drq; /* status register data request bit */
+ UINT8 busy_count; /* how long to keep busy bit set */
+
+ UINT8 buffer[6144]; /* I/O buffer (holds up to a whole track) */
+ UINT32 data_offset; /* offset into I/O buffer */
+ INT32 data_count; /* transfer count from/into I/O buffer */
+
+ UINT8 *fmt_sector_data[256]; /* pointer to data after formatting a track */
+
+ UINT8 dam_list[256][4]; /* list of data address marks while formatting */
+ int dam_data[256]; /* offset to data inside buffer while formatting */
+ int dam_cnt; /* valid number of entries in the dam_list */
+ UINT16 sector_length; /* sector length (byte) */
+
+ UINT8 ddam; /* ddam of sector found - used when reading */
+ UINT8 sector_data_id;
+ int data_direction;
+
+ int hld_count; /* head loaded counter */
+
+ /* timers to delay execution/completion of commands */
+ emu_timer *timer_cmd, *timer_data, *timer_rs, *timer_ws;
+
+ /* this is the drive currently selected */
+ device_t *drive;
+
+ /* this is the head currently selected */
+ UINT8 hd;
+
+ /* pause time when writing/reading sector */
+ int pause_time;
+
+ /* Were we busy when we received a FORCE_INT command */
+ UINT8 was_busy;
+
+ /* Pointer to interface */
+ const wd17xx_interface *intf;
+};
+
+
+/***************************************************************************
+ DEFAULT INTERFACES
+***************************************************************************/
+
+const wd17xx_interface default_wd17xx_interface =
+{
+ DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, { FLOPPY_0, FLOPPY_1, FLOPPY_2, FLOPPY_3}
+};
+
+const wd17xx_interface default_wd17xx_interface_2_drives =
+{
+ DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, { FLOPPY_0, FLOPPY_1, NULL, NULL}
+};
+
+
+/***************************************************************************
+ PROTOTYPES
+***************************************************************************/
+
+static void wd17xx_complete_command(device_t *device, int delay);
+static void wd17xx_timed_data_request(device_t *device);
+static int wd17xx_locate_sector(device_t *device);
+static void wd17xx_timed_read_sector_request(device_t *device);
+
+
+/*****************************************************************************
+ INLINE FUNCTIONS
+*****************************************************************************/
+
+INLINE wd1770_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == FD1771 || device->type() == FD1781 ||
+ device->type() == FD1791 || device->type() == FD1792 || device->type() == FD1793 || device->type() == FD1794 || device->type() == FD1795 || device->type() == FD1797 ||
+ device->type() == FD1761 || device->type() == FD1762 || device->type() == FD1763 || device->type() == FD1764 || device->type() == FD1765 || device->type() == FD1767 ||
+ device->type() == WD2791 || device->type() == WD2793 || device->type() == WD2795 || device->type() == WD2797 ||
+ device->type() == WD1770 || device->type() == WD1772 || device->type() == WD1773 ||
+ device->type() == MB8866 || device->type() == MB8876 || device->type() == MB8877);
+
+ return (wd1770_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+
+/***************************************************************************
+ HELPER FUNCTIONS
+***************************************************************************/
+
+static int wd17xx_has_dal(device_t *device)
+{
+ return (device->type() == FD1793 || device->type() == FD1794 || device->type() == FD1797 ||
+ device->type() == FD1763 || device->type() == FD1764 || device->type() == FD1767 ||
+ device->type() == WD1770 || device->type() == WD1772 || device->type() == WD1773 ||
+ device->type() == WD2793 || device->type() == WD2797 ||
+ device->type() == MB8877);
+}
+
+static int wd17xx_is_sd_only(device_t *device)
+{
+ return (device->type() == FD1771 || device->type() == FD1792 || device->type() == FD1794 || device->type() == FD1762 || device->type() == FD1764);
+}
+
+static int wd17xx_has_side_select(device_t *device)
+{
+ return (device->type() == FD1795 || device->type() == FD1797 ||
+ device->type() == FD1765 || device->type() == FD1767 ||
+ device->type() == WD2795 || device->type() == WD2797);
+}
+
+static int wd17xx_dden(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ if (!w->in_dden_func.isnull())
+ return w->in_dden_func();
+ else
+ return w->dden;
+}
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/* clear a data request */
+static void wd17xx_clear_drq(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ w->status &= ~STA_2_DRQ;
+
+ w->drq = CLEAR_LINE;
+ w->out_drq_func(w->drq);
+}
+
+/* set data request */
+static void wd17xx_set_drq(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ if (w->status & STA_2_DRQ)
+ w->status |= STA_2_LOST_DAT;
+
+ w->status |= STA_2_DRQ;
+
+ w->drq = ASSERT_LINE;
+ w->out_drq_func(w->drq);
+}
+
+/* clear interrupt request */
+static void wd17xx_clear_intrq(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ w->intrq = CLEAR_LINE;
+ w->out_intrq_func(w->intrq);
+}
+
+/* set interrupt request */
+static void wd17xx_set_intrq(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ w->status &= ~STA_2_BUSY;
+ w->status &= ~STA_2_DRQ;
+
+ w->intrq = ASSERT_LINE;
+ w->out_intrq_func(w->intrq);
+}
+
+/* set intrq after delay */
+static TIMER_CALLBACK( wd17xx_command_callback )
+{
+ device_t *device = (device_t *)ptr;
+ wd1770_state *w = get_safe_token(device);
+
+ if (w->last_command_data != FDC_FORCE_INT)
+ {
+ wd17xx_set_intrq(device);
+ }
+}
+
+/* write next byte to data register and set drq */
+static TIMER_CALLBACK( wd17xx_data_callback )
+{
+ device_t *device = (device_t *)ptr;
+ wd1770_state *w = get_safe_token(device);
+
+ /* check if this is a write command */
+ if( (w->command_type == TYPE_II && w->command == FDC_WRITE_SEC) ||
+ (w->command_type == TYPE_III && w->command == FDC_WRITE_TRK) )
+ {
+ /* we are ready for new data */
+ wd17xx_set_drq(device);
+
+ return;
+ }
+
+ /* any bytes remaining? */
+ if (w->data_count >= 1)
+ {
+ /* yes */
+ w->data = w->buffer[w->data_offset++];
+
+ if (VERBOSE_DATA)
+ logerror("wd17xx_data_callback: $%02X (data_count %d)\n", w->data, w->data_count);
+
+ wd17xx_set_drq(device);
+
+ /* any bytes remaining? */
+ if (--w->data_count < 1)
+ {
+ /* no */
+ w->data_offset = 0;
+
+ /* clear ddam type */
+ w->status &=~STA_2_REC_TYPE;
+
+ /* read a sector with ddam set? */
+ if (w->command_type == TYPE_II && w->ddam != 0)
+ {
+ /* set it */
+ w->status |= STA_2_REC_TYPE;
+ }
+
+ /* check if we should handle the next sector for a multi record read */
+ if (w->command_type == TYPE_II && w->command == FDC_READ_SEC && (w->read_cmd & 0x10))
+ {
+ if (VERBOSE)
+ logerror("wd17xx_data_callback: multi sector read\n");
+
+ if (w->sector == 0xff)
+ w->sector = 0x01;
+ else
+ w->sector++;
+
+ wd17xx_timed_read_sector_request(device);
+ }
+ else
+ {
+ /* Delay the INTRQ 3 byte times because we need to read two CRC bytes and
+ compare them with a calculated CRC */
+ wd17xx_complete_command(device, DELAY_DATADONE);
+
+ if (VERBOSE)
+ logerror("wd17xx_data_callback: data read completed\n");
+ }
+ }
+ else
+ {
+ /* requeue us for more data */
+ w->timer_data->adjust(attotime::from_usec(wd17xx_dden(device) ? 128 : 32));
+ }
+ }
+ else
+ {
+ logerror("wd17xx_data_callback: (no new data) $%02X (data_count %d)\n", w->data, w->data_count);
+ }
+}
+
+
+static void wd17xx_set_busy(device_t *device, attotime duration)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ w->status |= STA_1_BUSY;
+
+ w->timer_cmd->adjust(duration);
+}
+
+
+/* BUSY COUNT DOESN'T WORK PROPERLY! */
+
+static void wd17xx_command_restore(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+ UINT8 step_counter;
+
+ if (w->drive == NULL)
+ return;
+
+ step_counter = 255;
+
+#if 0
+ w->status |= STA_1_BUSY;
+#endif
+
+ /* setup step direction */
+ w->direction = -1;
+
+ w->command_type = TYPE_I;
+
+ /* reset busy count */
+ w->busy_count = 0;
+
+ if (1) // image_slotexists(w->drive) : FIXME
+ {
+ /* keep stepping until track 0 is received or 255 steps have been done */
+ while (floppy_tk00_r(w->drive) && (step_counter != 0))
+ {
+ /* update time to simulate seek time busy signal */
+ w->busy_count++;
+ floppy_drive_seek(w->drive, w->direction);
+ step_counter--;
+ }
+ }
+
+ /* update track reg */
+ w->track = 0;
+#if 0
+ /* simulate seek time busy signal */
+ w->busy_count = 0; //w->busy_count * ((w->data & FDC_STEP_RATE) + 1);
+
+ /* when command completes set irq */
+ wd17xx_set_intrq(device);
+#endif
+ wd17xx_set_busy(device, attotime::from_usec(100));
+}
+
+/*
+ Write an entire track. Formats which do not define a write_track
+ function pointer will cause a silent return.
+ What is written to the image depends on the selected format. Sector
+ dumps have to extract the sectors in the track, while track dumps
+ may directly write the bytes.
+ (The if-part below may thus be removed.)
+*/
+static void write_track(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+ floppy_image_legacy *floppy;
+#if 0
+ int i;
+ for (i=0;i+4<w->data_offset;)
+ {
+ if (w->buffer[i]==0xfe)
+ {
+ /* got address mark */
+ int track = w->buffer[i+1];
+ int side = w->buffer[i+2];
+ int sector = w->buffer[i+3];
+ //int len = w->buffer[i+4];
+ int filler = 0xe5; /* IBM and Thomson */
+ int density = w->density;
+ floppy_drive_format_sector(w->drive,side,sector,track,
+ w->hd,sector,density?1:0,filler);
+ i += 128; /* at least... */
+ }
+ else
+ i++;
+ }
+#endif
+
+ /* Get the size in bytes of the current track. For real hardware this
+ may vary per system in small degree, and there even for each track
+ and head, so we should not assume a fixed value here.
+ As we are using a buffered track writing, we have to find out how long
+ the track will become. The only object which can tell us is the
+ selected format.
+ */
+ w->data_count = 0;
+ floppy = flopimg_get_image(w->drive);
+ if (floppy != NULL)
+ w->data_count = floppy_get_track_size(floppy, w->hd, w->track);
+
+ if (w->data_count==0)
+ {
+ if (wd17xx_is_sd_only(device))
+ w->data_count = TRKSIZE_SD;
+ else
+ w->data_count = wd17xx_dden(device) ? TRKSIZE_SD : TRKSIZE_DD;
+ }
+
+ floppy_drive_write_track_data_info_buffer( w->drive, w->hd, (char *)w->buffer, &(w->data_count) );
+
+ w->data_offset = 0;
+
+ wd17xx_set_drq(device);
+ w->status |= STA_2_BUSY;
+ w->busy_count = 0;
+}
+
+/*
+ Read an entire track. It is up to the format to deliver the data. Sector
+ dumps may be required to fantasize the missing track bytes, while track
+ dumps can directly deliver them.
+ (The if-part below may thus be removed.)
+*/
+static void read_track(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+ floppy_image_legacy *floppy;
+#if 0
+ UINT8 *psrc; /* pointer to track format structure */
+ UINT8 *pdst; /* pointer to track buffer */
+ int cnt; /* number of bytes to fill in */
+ UINT16 crc; /* id or data CRC */
+ UINT8 d; /* data */
+ UINT8 t = w->track; /* track of DAM */
+ UINT8 h = w->head; /* head of DAM */
+ UINT8 s = w->sector_dam; /* sector of DAM */
+ UINT16 l = w->sector_length; /* sector length of DAM */
+ int i;
+
+ for (i = 0; i < w->sec_per_track; i++)
+ {
+ w->dam_list[i][0] = t;
+ w->dam_list[i][1] = h;
+ w->dam_list[i][2] = i;
+ w->dam_list[i][3] = l >> 8;
+ }
+
+ pdst = w->buffer;
+
+ if (w->density)
+ {
+ psrc = track_DD[0]; /* double density track format */
+ cnt = TRKSIZE_DD;
+ }
+ else
+ {
+ psrc = track_SD[0]; /* single density track format */
+ cnt = TRKSIZE_SD;
+ }
+
+ while (cnt > 0)
+ {
+ if (psrc[0] == 0) /* no more track format info ? */
+ {
+ if (w->dam_cnt < w->sec_per_track) /* but more DAM info ? */
+ {
+ if (w->density)/* DD track ? */
+ psrc = track_DD[3];
+ else
+ psrc = track_SD[3];
+ }
+ }
+
+ if (psrc[0] != 0) /* more track format info ? */
+ {
+ cnt -= psrc[0]; /* subtract size */
+ d = psrc[1];
+
+ if (d == 0xf5) /* clear CRC ? */
+ {
+ crc = 0xffff;
+ d = 0xa1; /* store A1 */
+ }
+
+ for (i = 0; i < *psrc; i++)
+ *pdst++ = d; /* fill data */
+
+ if (d == 0xf7) /* store CRC ? */
+ {
+ pdst--; /* go back one byte */
+ *pdst++ = crc & 255; /* put CRC low */
+ *pdst++ = crc / 256; /* put CRC high */
+ cnt -= 1; /* count one more byte */
+ }
+ else if (d == 0xfe)/* address mark ? */
+ {
+ crc = 0xffff; /* reset CRC */
+ }
+ else if (d == 0x80)/* sector ID ? */
+ {
+ pdst--; /* go back one byte */
+ t = *pdst++ = w->dam_list[w->dam_cnt][0]; /* track number */
+ h = *pdst++ = w->dam_list[w->dam_cnt][1]; /* head number */
+ s = *pdst++ = w->dam_list[w->dam_cnt][2]; /* sector number */
+ l = *pdst++ = w->dam_list[w->dam_cnt][3]; /* sector length code */
+ w->dam_cnt++;
+ crc = ccitt_crc16_one(crc, t); /* build CRC */
+ crc = ccitt_crc16_one(crc, h); /* build CRC */
+ crc = ccitt_crc16_one(crc, s); /* build CRC */
+ crc = ccitt_crc16_one(crc, l); /* build CRC */
+ l = (l == 0) ? 128 : l << 8;
+ }
+ else if (d == 0xfb)// data address mark ?
+ {
+ crc = 0xffff; // reset CRC
+ }
+ else if (d == 0x81)// sector DATA ?
+ {
+ pdst--; /* go back one byte */
+ if (seek(w, t, h, s) == 0)
+ {
+ if (mame_fread(w->image_file, pdst, l) != l)
+ {
+ w->status = STA_2_CRC_ERR;
+ return;
+ }
+ }
+ else
+ {
+ w->status = STA_2_REC_N_FND;
+ return;
+ }
+ for (i = 0; i < l; i++) // build CRC of all data
+ crc = ccitt_crc16_one(crc, *pdst++);
+ cnt -= l;
+ }
+ psrc += 2;
+ }
+ else
+ {
+ *pdst++ = 0xff; /* fill track */
+ cnt--; /* until end */
+ }
+ }
+#endif
+ /* Determine the track size. We cannot allow different sizes in this
+ design (see above, write_track). */
+ w->data_count = 0;
+ floppy = flopimg_get_image(w->drive);
+ if (floppy != NULL)
+ w->data_count = floppy_get_track_size(floppy, w->hd, w->track);
+
+ if (w->data_count==0)
+ {
+ if (wd17xx_is_sd_only(device))
+ w->data_count = TRKSIZE_SD;
+ else
+ w->data_count = wd17xx_dden(device) ? TRKSIZE_SD : TRKSIZE_DD;
+ }
+
+ floppy_drive_read_track_data_info_buffer( w->drive, w->hd, (char *)w->buffer, &(w->data_count) );
+
+ w->data_offset = 0;
+
+ wd17xx_set_drq(device);
+ w->status |= STA_2_BUSY;
+ w->busy_count = 0;
+}
+
+
+/* read the next data address mark */
+static void wd17xx_read_id(device_t *device)
+{
+ chrn_id id;
+ wd1770_state *w = get_safe_token(device);
+
+ w->status &= ~(STA_2_CRC_ERR | STA_2_REC_N_FND);
+
+ /* get next id from disc */
+ if (floppy_drive_get_next_id(w->drive, w->hd, &id))
+ {
+ UINT16 crc = 0xffff;
+
+ w->data_offset = 0;
+ w->data_count = 6;
+
+ /* for MFM */
+ /* crc includes 3x0x0a1, and 1x0x0fe (id mark) */
+ crc = ccitt_crc16_one(crc,0x0a1);
+ crc = ccitt_crc16_one(crc,0x0a1);
+ crc = ccitt_crc16_one(crc,0x0a1);
+ crc = ccitt_crc16_one(crc,0x0fe);
+
+ w->buffer[0] = id.C;
+ w->buffer[1] = id.H;
+ w->buffer[2] = id.R;
+ w->buffer[3] = id.N;
+ crc = ccitt_crc16_one(crc, w->buffer[0]);
+ crc = ccitt_crc16_one(crc, w->buffer[1]);
+ crc = ccitt_crc16_one(crc, w->buffer[2]);
+ crc = ccitt_crc16_one(crc, w->buffer[3]);
+ /* crc is stored hi-byte followed by lo-byte */
+ w->buffer[4] = crc>>8;
+ w->buffer[5] = crc & 255;
+
+ w->sector = id.C;
+
+ if (VERBOSE)
+ logerror("wd17xx_read_id: read id succeeded.\n");
+
+ wd17xx_timed_data_request(device);
+ }
+ else
+ {
+ /* record not found */
+ w->status |= STA_2_REC_N_FND;
+ //w->sector = w->track;
+ if (VERBOSE)
+ logerror("wd17xx_read_id: read id failed\n");
+
+ wd17xx_complete_command(device, DELAY_ERROR);
+ }
+}
+
+
+
+void wd17xx_index_pulse_callback(device_t *controller, device_t *img, int state)
+{
+ wd1770_state *w = get_safe_token(controller);
+
+ if (img != w->drive)
+ return;
+
+ w->idx = state;
+
+ if (!state && w->idx && BIT(w->interrupt, 2))
+ wd17xx_set_intrq(controller);
+
+ if (w->hld_count)
+ w->hld_count--;
+}
+
+
+
+static int wd17xx_locate_sector(device_t *device)
+{
+ UINT8 revolution_count;
+ chrn_id id;
+ wd1770_state *w = get_safe_token(device);
+
+ revolution_count = 0;
+
+ w->status &= ~STA_2_REC_N_FND;
+
+ while (revolution_count!=4)
+ {
+ if (floppy_drive_get_next_id(w->drive, w->hd, &id))
+ {
+ /* compare track */
+ if (id.C == w->track)
+ {
+ /* compare head, if we were asked to */
+ if (!wd17xx_has_side_select(device) || (id.H == w->head) || (w->head == (UINT8) ~0))
+ {
+ /* compare id */
+ if (id.R == w->sector)
+ {
+ w->sector_length = 1<<(id.N+7);
+ w->sector_data_id = id.data_id;
+ /* get ddam status */
+ w->ddam = id.flags & ID_FLAG_DELETED_DATA;
+ /* got record type here */
+ if (VERBOSE)
+ logerror("sector found! C:$%02x H:$%02x R:$%02x N:$%02x%s\n", id.C, id.H, id.R, id.N, w->ddam ? " DDAM" : "");
+ return 1;
+ }
+ }
+ }
+ }
+
+ /* index set? */
+ if (floppy_drive_get_flag_state(w->drive, FLOPPY_DRIVE_INDEX))
+ {
+ /* update revolution count */
+ revolution_count++;
+ }
+ }
+ return 0;
+}
+
+
+static int wd17xx_find_sector(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+ if ( wd17xx_locate_sector(device) )
+ {
+ return 1;
+ }
+
+ /* record not found */
+ w->status |= STA_2_REC_N_FND;
+
+ if (VERBOSE)
+ logerror("track %d sector %d not found!\n", w->track, w->sector);
+
+ wd17xx_complete_command(device, DELAY_ERROR);
+
+ return 0;
+}
+
+
+
+/* read a sector */
+static void wd17xx_read_sector(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+ w->data_offset = 0;
+
+ /* side compare? */
+ if (w->read_cmd & 0x02)
+ w->head = (w->read_cmd & 0x08) ? 1 : 0;
+ else
+ w->head = ~0;
+
+ if (wd17xx_find_sector(device))
+ {
+ w->data_count = w->sector_length;
+
+ /* read data */
+ floppy_drive_read_sector_data(w->drive, w->hd, w->sector_data_id, (char *)w->buffer, w->sector_length);
+
+ wd17xx_timed_data_request(device);
+
+ w->status |= STA_2_BUSY;
+ w->busy_count = 0;
+ }
+}
+
+
+/* called on error, or when command is actually completed */
+/* KT - I have used a timer for systems that use interrupt driven transfers.
+A interrupt occurs after the last byte has been read. If it occurs at the time
+when the last byte has been read it causes problems - same byte read again
+or bytes missed */
+/* TJL - I have add a parameter to allow the emulation to specify the delay
+*/
+static void wd17xx_complete_command(device_t *device, int delay)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ w->data_count = 0;
+
+ w->hld_count = 2;
+
+ /* set new timer */
+ int usecs = wd17xx_dden(device) ? 32 : 16;
+ w->timer_cmd->adjust(attotime::from_usec(usecs));
+
+ /* Kill onshot read/write sector timers */
+ w->timer_rs->adjust(attotime::never);
+ w->timer_ws->adjust(attotime::never);
+}
+
+
+
+static void wd17xx_write_sector(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+ /* at this point, the disc is write enabled, and data
+ * has been transfered into our buffer - now write it to
+ * the disc image or to the real disc
+ */
+
+ /* side compare? */
+ if (w->write_cmd & 0x02)
+ w->head = (w->write_cmd & 0x08) ? 1 : 0;
+ else
+ w->head = ~0;
+
+ /* find sector */
+ if (wd17xx_find_sector(device))
+ {
+ w->data_count = w->sector_length;
+
+ /* write data */
+ floppy_drive_write_sector_data(w->drive, w->hd, w->sector_data_id, (char *)w->buffer, w->sector_length, w->write_cmd & 0x01);
+ }
+}
+
+
+
+/* verify the seek operation by looking for a id that has a matching track value */
+static void wd17xx_verify_seek(device_t *device)
+{
+ UINT8 revolution_count;
+ chrn_id id;
+ wd1770_state *w = get_safe_token(device);
+
+ revolution_count = 0;
+
+ if (VERBOSE)
+ logerror("doing seek verify\n");
+
+ w->status &= ~STA_1_SEEK_ERR;
+
+ /* must be found within 5 revolutions otherwise error */
+ while (revolution_count!=5)
+ {
+ if (floppy_drive_get_next_id(w->drive, w->hd, &id))
+ {
+ /* compare track */
+ if (id.C == w->track)
+ {
+ if (VERBOSE)
+ logerror("seek verify succeeded!\n");
+ return;
+ }
+ }
+
+ /* index set? */
+ if (floppy_drive_get_flag_state(w->drive, FLOPPY_DRIVE_INDEX))
+ {
+ /* update revolution count */
+ revolution_count++;
+ }
+ }
+
+ w->status |= STA_1_SEEK_ERR;
+
+ if (VERBOSE)
+ logerror("failed seek verify!\n");
+}
+
+
+
+/* callback to initiate read sector */
+static TIMER_CALLBACK( wd17xx_read_sector_callback )
+{
+ device_t *device = (device_t *)ptr;
+ wd1770_state *w = get_safe_token(device);
+
+ /* ok, start that read! */
+
+ if (VERBOSE)
+ logerror("wd179x: Read Sector callback.\n");
+
+ if (!floppy_drive_get_flag_state(w->drive, FLOPPY_DRIVE_READY))
+ wd17xx_complete_command(device, DELAY_NOTREADY);
+ else
+ wd17xx_read_sector(device);
+}
+
+
+
+/* callback to initiate write sector */
+static TIMER_CALLBACK( wd17xx_write_sector_callback )
+{
+ device_t *device = (device_t *)ptr;
+ wd1770_state *w = get_safe_token(device);
+
+ /* ok, start that write! */
+
+ if (VERBOSE)
+ logerror("wd179x: Write Sector callback.\n");
+
+ if (!floppy_drive_get_flag_state(w->drive, FLOPPY_DRIVE_READY))
+ wd17xx_complete_command(device, DELAY_NOTREADY);
+ else
+ {
+
+ /* drive write protected? */
+ if (floppy_wpt_r(w->drive) == CLEAR_LINE)
+ {
+ w->status |= STA_2_WRITE_PRO;
+
+ wd17xx_complete_command(device, DELAY_ERROR);
+ }
+ else
+ {
+ /* side compare? */
+ if (w->write_cmd & 0x02)
+ w->head = (w->write_cmd & 0x08) ? 1 : 0;
+ else
+ w->head = ~0;
+
+ /* attempt to find it first before getting data from cpu */
+ if (wd17xx_find_sector(device))
+ {
+ /* request data */
+ w->data_offset = 0;
+ w->data_count = w->sector_length;
+
+ wd17xx_set_drq(device);
+
+ w->status |= STA_2_BUSY;
+ w->busy_count = 0;
+ }
+ }
+ }
+}
+
+
+
+/* setup a timed data request - data request will be triggered in a few usecs time */
+static void wd17xx_timed_data_request(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ /* set new timer */
+ w->timer_data->adjust(attotime::from_usec(wd17xx_dden(device) ? 128 : 32));
+}
+
+
+
+/* setup a timed read sector - read sector will be triggered in a few usecs time */
+static void wd17xx_timed_read_sector_request(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ /* set new timer */
+ w->timer_rs->adjust(attotime::from_usec(w->pause_time));
+}
+
+
+
+/* setup a timed write sector - write sector will be triggered in a few usecs time */
+static void wd17xx_timed_write_sector_request(device_t *device)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ /* set new timer */
+ w->timer_ws->adjust(attotime::from_usec(w->pause_time));
+}
+
+
+/***************************************************************************
+ INTERFACE
+***************************************************************************/
+
+/* use this to determine which drive is controlled by WD */
+void wd17xx_set_drive(device_t *device, UINT8 drive)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ if (VERBOSE)
+ logerror("wd17xx_set_drive: $%02x\n", drive);
+
+ if (w->intf->floppy_drive_tags[drive] != NULL)
+ {
+ if (device->owner() != NULL) {
+ w->drive = device->owner()->subdevice(w->intf->floppy_drive_tags[drive]);
+ if (w->drive == NULL) {
+ w->drive = device->machine().device(w->intf->floppy_drive_tags[drive]);
+ }
+ }
+ else
+ w->drive = device->machine().device(w->intf->floppy_drive_tags[drive]);
+ }
+}
+
+void wd17xx_set_side(device_t *device, UINT8 head)
+{
+ wd1770_state *w = get_safe_token(device);
+
+ if (VERBOSE)
+ {
+ if (head != w->hd)
+ logerror("wd17xx_set_side: $%02x\n", head);
+ }
+
+ w->hd = head;
+}
+
+void wd17xx_set_pause_time(device_t *device, int usec)
+{
+ wd1770_state *w = get_safe_token(device);
+ w->pause_time = usec;
+}
+
+
+/***************************************************************************
+ DEVICE HANDLERS
+***************************************************************************/
+
+/* master reset */
+WRITE_LINE_DEVICE_HANDLER( wd17xx_mr_w )
+{
+ wd1770_state *w = get_safe_token(device);
+
+ /* reset device when going from high to low */
+ if (w->mr && state == CLEAR_LINE)
+ {
+ w->command = 0x03;
+ w->status &= ~STA_1_NOT_READY; /* ? */
+ }
+
+ /* execute restore command when going from low to high */
+ if (w->mr == CLEAR_LINE && state)
+ {
+ wd17xx_command_restore(device);
+ w->sector = 0x01;
+ }
+
+ w->mr = state;
+}
+
+/* ready and enable precomp (1773 only) */
+WRITE_LINE_DEVICE_HANDLER( wd17xx_rdy_w )
+{
+ wd1770_state *w = get_safe_token(device);
+ w->rdy = state;
+}
+
+/* motor on, 1770 and 1772 only */
+READ_LINE_DEVICE_HANDLER( wd17xx_mo_r )
+{
+ wd1770_state *w = get_safe_token(device);
+ return w->mo;
+}
+
+/* track zero */
+WRITE_LINE_DEVICE_HANDLER( wd17xx_tr00_w )
+{
+ wd1770_state *w = get_safe_token(device);
+ w->tr00 = state;
+}
+
+/* index pulse */
+WRITE_LINE_DEVICE_HANDLER( wd17xx_idx_w )
+{
+ wd1770_state *w = get_safe_token(device);
+ w->idx = state;
+
+ if (!state && w->idx && BIT(w->interrupt, 2))
+ wd17xx_set_intrq(device);
+}
+
+/* write protect status */
+WRITE_LINE_DEVICE_HANDLER( wd17xx_wprt_w )
+{
+ wd1770_state *w = get_safe_token(device);
+ w->wprt = state;
+}
+
+/* double density enable */
+WRITE_LINE_DEVICE_HANDLER( wd17xx_dden_w )
+{
+ wd1770_state *w = get_safe_token(device);
+
+ /* not supported on FD1771, FD1792, FD1794, FD1762 and FD1764 */
+ if (wd17xx_is_sd_only(device))
+ fatalerror("wd17xx_dden_w: double density input not supported on this model!");
+ else if (!w->in_dden_func.isnull())
+ logerror("wd17xx_dden_w: write has no effect because a read handler is already defined!\n");
+ else
+ w->dden = state;
+}
+
+/* data request */
+READ_LINE_DEVICE_HANDLER( wd17xx_drq_r )
+{
+ wd1770_state *w = get_safe_token(device);
+ return w->drq;
+}
+
+/* interrupt request */
+READ_LINE_DEVICE_HANDLER( wd17xx_intrq_r )
+{
+ wd1770_state *w = get_safe_token(device);
+ return w->intrq;
+}
+
+/* read the FDC status register. This clears IRQ line too */
+READ8_DEVICE_HANDLER( wd17xx_status_r )
+{
+ wd1770_state *w = get_safe_token(device);
+ int result;
+
+ if (!BIT(w->interrupt, 3))
+ {
+ wd17xx_clear_intrq(device);
+ }
+
+ /* bit 7, 'not ready' or 'motor on' */
+ if (device->type() == WD1770 || device->type() == WD1772)
+ {
+ w->status &= ~STA_1_MOTOR_ON;
+ w->status |= w->mo << 7;
+ }
+ else
+ {
+ w->status &= ~STA_1_NOT_READY;
+ if (!floppy_drive_get_flag_state(w->drive, FLOPPY_DRIVE_READY))
+ w->status |= STA_1_NOT_READY;
+ }
+
+ result = w->status;
+
+ /* type 1 command or force int command? */
+ if ((w->command_type==TYPE_I) || (w->command_type==TYPE_IV && ! w->was_busy))
+ {
+ result &= ~(STA_1_IPL | STA_1_TRACK0);
+
+ /* bit 1, index pulse */
+ result |= w->idx << 1;
+
+ /* bit 2, track 0 state, inverted */
+ result |= !floppy_tk00_r(w->drive) << 2;
+
+ if (w->command_type==TYPE_I)
+ {
+ if (w->hld_count)
+ w->status |= STA_1_HD_LOADED;
+ else
+ w->status &= ~ STA_1_HD_LOADED;
+ }
+
+ /* bit 6, write protect, inverted */
+ result |= !floppy_wpt_r(w->drive) << 6;
+ }
+
+ /* eventually set data request bit */
+// w->status |= w->status_drq;
+
+ if (VERBOSE)
+ {
+ if (w->data_count < 4)
+ logerror("%s: wd17xx_status_r: $%02X (data_count %d)\n", device->machine().describe_context(), result, w->data_count);
+ }
+
+ return result ^ (wd17xx_has_dal(device) ? 0 : 0xff);
+}
+
+/* read the FDC track register */
+READ8_DEVICE_HANDLER( wd17xx_track_r )
+{
+ wd1770_state *w = get_safe_token(device);
+
+ if (VERBOSE)
+ logerror("%s: wd17xx_track_r: $%02X\n", device->machine().describe_context(), w->track);
+
+ return w->track ^ (wd17xx_has_dal(device) ? 0 : 0xff);
+}
+
+/* read the FDC sector register */
+READ8_DEVICE_HANDLER( wd17xx_sector_r )
+{
+ wd1770_state *w = get_safe_token(device);
+
+ if (VERBOSE)
+ logerror("%s: wd17xx_sector_r: $%02X\n", device->machine().describe_context(), w->sector);
+
+ return w->sector ^ (wd17xx_has_dal(device) ? 0 : 0xff);
+}
+
+/* read the FDC data register */
+READ8_DEVICE_HANDLER( wd17xx_data_r )
+{
+ wd1770_state *w = get_safe_token(device);
+
+ if (VERBOSE_DATA)
+ logerror("%s: wd17xx_data_r: %02x\n", device->machine().describe_context(), w->data);
+
+ /* clear data request */
+ wd17xx_clear_drq(device);
+
+ return w->data ^ (wd17xx_has_dal(device) ? 0 : 0xff);
+}
+
+/* write the FDC command register */
+WRITE8_DEVICE_HANDLER( wd17xx_command_w )
+{
+ wd1770_state *w = get_safe_token(device);
+ if (!wd17xx_has_dal(device)) data ^= 0xff;
+
+ w->last_command_data = data;
+
+ /* only the WD1770 and WD1772 have a 'motor on' line */
+ if (device->type() == WD1770 || device->type() == WD1772)
+ {
+ w->mo = ASSERT_LINE;
+ floppy_mon_w(w->drive, CLEAR_LINE);
+ }
+
+ floppy_drive_set_ready_state(w->drive, 1,0);
+
+ if (!BIT(w->interrupt, 3))
+ {
+ wd17xx_clear_intrq(device);
+ }
+
+ /* clear write protected. On read sector, read track and read dam, write protected bit is clear */
+ w->status &= ~((1<<6) | (1<<5) | (1<<4));
+
+ if ((data & ~FDC_MASK_TYPE_IV) == FDC_FORCE_INT)
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X FORCE_INT (data_count %d)\n", device->machine().describe_context(), data, w->data_count);
+
+ w->data_count = 0;
+ w->data_offset = 0;
+ w->was_busy = w->status & STA_2_BUSY;
+ w->status &= ~STA_2_BUSY;
+
+ wd17xx_clear_drq(device);
+
+ if (!BIT(w->interrupt, 3) && BIT(data, 3))
+ {
+ /* set immediate interrupt */
+ wd17xx_set_intrq(device);
+ }
+
+ if (BIT(w->interrupt, 3))
+ {
+ if (data == FDC_FORCE_INT)
+ {
+ /* clear immediate interrupt */
+ w->interrupt = data & 0x0f;
+ }
+ else
+ {
+ /* keep immediate interrupt */
+ w->interrupt = 0x08 | (data & 0x07);
+ }
+ }
+ else
+ {
+ w->interrupt = data & 0x0f;
+ }
+
+ /* terminate command */
+ wd17xx_complete_command(device, DELAY_ERROR);
+
+ w->busy_count = 0;
+ w->command_type = TYPE_IV;
+ return;
+ }
+
+ if (data & 0x80)
+ {
+ /*w->status_ipl = 0;*/
+
+ if ((data & ~FDC_MASK_TYPE_II) == FDC_READ_SEC)
+ {
+ if (VERBOSE)
+ {
+ logerror("%s: wd17xx_command_w $%02X READ_SEC (", device->machine().describe_context(), data);
+ logerror("cmd=%02X, trk=%02X, sec=%02X, dat=%02X)\n",w->command,w->track,w->sector,w->data);
+ }
+ w->read_cmd = data;
+ w->command = data & ~FDC_MASK_TYPE_II;
+ w->command_type = TYPE_II;
+ w->status &= ~STA_2_LOST_DAT;
+ w->status |= STA_2_BUSY;
+ wd17xx_clear_drq(device);
+
+ wd17xx_timed_read_sector_request(device);
+
+ return;
+ }
+
+ if ((data & ~FDC_MASK_TYPE_II) == FDC_WRITE_SEC)
+ {
+ if (VERBOSE)
+ {
+ logerror("%s: wd17xx_command_w $%02X WRITE_SEC (", device->machine().describe_context(), data);
+ logerror("cmd=%02X, trk=%02X, sec=%02X, dat=%02X)\n",w->command,w->track,w->sector,w->data);
+ }
+
+ w->write_cmd = data;
+ w->command = data & ~FDC_MASK_TYPE_II;
+ w->command_type = TYPE_II;
+ w->status &= ~STA_2_LOST_DAT;
+ w->status |= STA_2_BUSY;
+ wd17xx_clear_drq(device);
+
+ wd17xx_timed_write_sector_request(device);
+
+ return;
+ }
+
+ if ((data & ~FDC_MASK_TYPE_III) == FDC_READ_TRK)
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X READ_TRK\n", device->machine().describe_context(), data);
+
+ w->command = data & ~FDC_MASK_TYPE_III;
+ w->command_type = TYPE_III;
+ w->status &= ~STA_2_LOST_DAT;
+ wd17xx_clear_drq(device);
+#if 1
+// w->status = seek(w, w->track, w->head, w->sector);
+ if (w->status == 0)
+ read_track(device);
+#endif
+ return;
+ }
+
+ if ((data & ~FDC_MASK_TYPE_III) == FDC_WRITE_TRK)
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X WRITE_TRK\n", device->machine().describe_context(), data);
+
+ w->command_type = TYPE_III;
+ w->status &= ~STA_2_LOST_DAT;
+ wd17xx_clear_drq(device);
+
+ if (!floppy_drive_get_flag_state(w->drive, FLOPPY_DRIVE_READY))
+ {
+ wd17xx_complete_command(device, DELAY_NOTREADY);
+ }
+ else
+ {
+
+ /* drive write protected? */
+ if (floppy_wpt_r(w->drive) == CLEAR_LINE)
+ {
+ /* yes */
+ w->status |= STA_2_WRITE_PRO;
+ /* quit command */
+ wd17xx_complete_command(device, DELAY_ERROR);
+ }
+ else
+ {
+ w->command = data & ~FDC_MASK_TYPE_III;
+ w->data_offset = 0;
+ if (wd17xx_is_sd_only(device))
+ w->data_count = TRKSIZE_SD;
+ else
+ w->data_count = wd17xx_dden(device) ? TRKSIZE_SD : TRKSIZE_DD;
+
+ wd17xx_set_drq(device);
+ w->status |= STA_2_BUSY;
+ w->busy_count = 0;
+ }
+ }
+ return;
+ }
+
+ if ((data & ~FDC_MASK_TYPE_III) == FDC_READ_DAM)
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X READ_DAM\n", device->machine().describe_context(), data);
+
+ w->command_type = TYPE_III;
+ w->command = data & ~FDC_MASK_TYPE_III;
+ w->status &= ~STA_2_LOST_DAT;
+ w->status |= STA_2_BUSY;
+
+ wd17xx_clear_drq(device);
+
+ if (floppy_drive_get_flag_state(w->drive, FLOPPY_DRIVE_READY))
+ wd17xx_read_id(device);
+ else
+ wd17xx_complete_command(device, DELAY_NOTREADY);
+
+ return;
+ }
+
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X unknown\n", device->machine().describe_context(), data);
+
+ return;
+ }
+
+ w->status |= STA_1_BUSY;
+
+ /* clear CRC error */
+ w->status &=~STA_1_CRC_ERR;
+
+ if ((data & ~FDC_MASK_TYPE_I) == FDC_RESTORE)
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X RESTORE\n", device->machine().describe_context(), data);
+
+ wd17xx_command_restore(device);
+ }
+
+ if ((data & ~FDC_MASK_TYPE_I) == FDC_SEEK)
+ {
+ UINT8 newtrack;
+
+ if (VERBOSE)
+ logerror("old track: $%02x new track: $%02x\n", w->track, w->data);
+ w->command_type = TYPE_I;
+
+ /* setup step direction */
+ if (w->track < w->data)
+ {
+ if (VERBOSE)
+ logerror("direction: +1\n");
+
+ w->direction = 1;
+ }
+ else if (w->track > w->data)
+ {
+ if (VERBOSE)
+ logerror("direction: -1\n");
+
+ w->direction = -1;
+ }
+
+ newtrack = w->data;
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X SEEK (data_reg is $%02X)\n", device->machine().describe_context(), data, newtrack);
+
+ /* reset busy count */
+ w->busy_count = 0;
+
+ /* keep stepping until reached track programmed */
+ while (w->track != newtrack)
+ {
+ /* update time to simulate seek time busy signal */
+ w->busy_count++;
+
+ /* update track reg */
+ w->track += w->direction;
+
+ floppy_drive_seek(w->drive, w->direction);
+ }
+
+ /* simulate seek time busy signal */
+ w->busy_count = 0; //w->busy_count * ((data & FDC_STEP_RATE) + 1);
+#if 0
+ wd17xx_set_intrq(device);
+#endif
+ wd17xx_set_busy(device, attotime::from_usec(100));
+
+ }
+
+ if ((data & ~(FDC_STEP_UPDATE | FDC_MASK_TYPE_I)) == FDC_STEP)
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X STEP dir %+d\n", device->machine().describe_context(), data, w->direction);
+
+ w->command_type = TYPE_I;
+ /* if it is a real floppy, issue a step command */
+ /* simulate seek time busy signal */
+ w->busy_count = 0; //((data & FDC_STEP_RATE) + 1);
+
+ floppy_drive_seek(w->drive, w->direction);
+
+ if (data & FDC_STEP_UPDATE)
+ w->track += w->direction;
+
+#if 0
+ wd17xx_set_intrq(device);
+#endif
+ wd17xx_set_busy(device, attotime::from_usec(100));
+
+
+ }
+
+ if ((data & ~(FDC_STEP_UPDATE | FDC_MASK_TYPE_I)) == FDC_STEP_IN)
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X STEP_IN\n", device->machine().describe_context(), data);
+
+ w->command_type = TYPE_I;
+ w->direction = +1;
+ /* simulate seek time busy signal */
+ w->busy_count = 0; //((data & FDC_STEP_RATE) + 1);
+
+ floppy_drive_seek(w->drive, w->direction);
+
+ if (data & FDC_STEP_UPDATE)
+ w->track += w->direction;
+#if 0
+ wd17xx_set_intrq(device);
+#endif
+ wd17xx_set_busy(device, attotime::from_usec(100));
+
+ }
+
+ if ((data & ~(FDC_STEP_UPDATE | FDC_MASK_TYPE_I)) == FDC_STEP_OUT)
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_command_w $%02X STEP_OUT\n", device->machine().describe_context(), data);
+
+ w->command_type = TYPE_I;
+ w->direction = -1;
+ /* simulate seek time busy signal */
+ w->busy_count = 0; //((data & FDC_STEP_RATE) + 1);
+
+ /* for now only allows a single drive to be selected */
+ floppy_drive_seek(w->drive, w->direction);
+
+ if (data & FDC_STEP_UPDATE)
+ w->track += w->direction;
+
+#if 0
+ wd17xx_set_intrq(device);
+#endif
+ wd17xx_set_busy(device, attotime::from_usec(100));
+ }
+
+ if (w->command_type == TYPE_I)
+ {
+ /* 0 is enable spin up sequence, 1 is disable spin up sequence */
+ if ((data & FDC_STEP_HDLOAD)==0)
+ {
+ w->status |= STA_1_HD_LOADED;
+ w->hld_count = 2;
+ }
+ else
+ w->status &= ~STA_1_HD_LOADED;
+
+ if (data & FDC_STEP_VERIFY)
+ {
+ /* verify seek */
+ wd17xx_verify_seek(device);
+ }
+ }
+}
+
+/* write the FDC track register */
+WRITE8_DEVICE_HANDLER( wd17xx_track_w )
+{
+ wd1770_state *w = get_safe_token(device);
+ if (!wd17xx_has_dal(device)) data ^= 0xff;
+
+ w->track = data;
+
+ if (VERBOSE)
+ logerror("%s: wd17xx_track_w $%02X\n", device->machine().describe_context(), data);
+}
+
+/* write the FDC sector register */
+WRITE8_DEVICE_HANDLER( wd17xx_sector_w )
+{
+ wd1770_state *w = get_safe_token(device);
+ if (!wd17xx_has_dal(device)) data ^= 0xff;
+
+ w->sector = data;
+
+ if (VERBOSE)
+ logerror("%s: wd17xx_sector_w $%02X\n", device->machine().describe_context(), data);
+}
+
+/* write the FDC data register */
+WRITE8_DEVICE_HANDLER( wd17xx_data_w )
+{
+ wd1770_state *w = get_safe_token(device);
+ if (!wd17xx_has_dal(device)) data ^= 0xff;
+
+ if (w->data_count > 0)
+ {
+ wd17xx_clear_drq(device);
+
+ /* put byte into buffer */
+ if (VERBOSE_DATA)
+ logerror("wd17xx_info buffered data: $%02X at offset %d.\n", data, w->data_offset);
+
+ w->buffer[w->data_offset++] = data;
+
+ if (--w->data_count < 1)
+ {
+ if (w->command == FDC_WRITE_TRK)
+ write_track(device);
+ else
+ wd17xx_write_sector(device);
+
+ w->data_offset = 0;
+
+ /* Check we should handle the next sector for a multi record write */
+ if ( w->command_type == TYPE_II && w->command == FDC_WRITE_SEC && ( w->write_cmd & FDC_MULTI_REC ) )
+ {
+ w->sector++;
+ if (wd17xx_locate_sector(device))
+ {
+ w->data_count = w->sector_length;
+
+ w->status |= STA_2_BUSY;
+ w->busy_count = 0;
+
+ wd17xx_timed_data_request(device);
+ }
+ }
+ else
+ {
+ wd17xx_complete_command(device, DELAY_DATADONE);
+ if (VERBOSE)
+ logerror("wd17xx_data_w(): multi data write completed\n");
+ }
+// wd17xx_complete_command(device, DELAY_DATADONE);
+ }
+ else
+ {
+ if (w->command == FDC_WRITE_TRK)
+ {
+ /* Process special data values according to WD17xx specification.
+ Note that as CRC values take up two bytes which are written on
+ every 0xf7 byte, this will cause the actual image to
+ grow larger than what was written from the system. So we need
+ to take the value of data_offset when writing the track.
+ */
+ if (wd17xx_dden(device))
+ {
+ switch (data)
+ {
+ case 0xf5:
+ case 0xf6:
+ /* not allowed in FM. */
+ /* Take back the last write. */
+ w->data_offset--;
+ break;
+ case 0xf7:
+ /* Take back the last write. */
+ w->data_offset--;
+ /* write two crc bytes */
+ w->buffer[w->data_offset++] = (w->crc>>8)&0xff;
+ w->buffer[w->data_offset++] = (w->crc&0xff);
+ w->crc_active = FALSE;
+ break;
+ case 0xf8:
+ case 0xf9:
+ case 0xfa:
+ case 0xfb:
+ case 0xfe:
+ /* Preset crc */
+ w->crc = 0xffff;
+ /* AM is included in the CRC */
+ w->crc = ccitt_crc16_one(w->crc, data);
+ w->crc_active = TRUE;
+ break;
+ case 0xfc:
+ /* Write index mark. No effect here as we do not store clock patterns.
+ Maybe later. */
+ break;
+ case 0xfd:
+ /* Just write, don't use for CRC. */
+ break;
+ default:
+ /* Byte already written. */
+ if (w->crc_active)
+ w->crc = ccitt_crc16_one(w->crc, data);
+ }
+ }
+ else /* MFM */
+ {
+ switch (data)
+ {
+ case 0xf5:
+ /* Take back the last write. */
+ w->data_offset--;
+ /* Write a1 */
+ w->buffer[w->data_offset++] = 0xa1;
+ /* Preset CRC */
+ w->crc = 0xffff;
+ w->crc_active = TRUE;
+ break;
+ case 0xf6:
+ /* Take back the last write. */
+ w->data_offset--;
+ /* Write c2 */
+ w->buffer[w->data_offset++] = 0xc2;
+ break;
+ case 0xf7:
+ /* Take back the last write. */
+ w->data_offset--;
+ /* write two crc bytes */
+ w->buffer[w->data_offset++] = (w->crc>>8)&0xff;
+ w->buffer[w->data_offset++] = (w->crc&0xff);
+ w->crc_active = FALSE;
+ break;
+ case 0xf8:
+ case 0xf9:
+ case 0xfa:
+ case 0xfb:
+ case 0xfc:
+ case 0xfd:
+ /* Just write, don't use for CRC. */
+ break;
+ case 0xfe:
+ /* AM is included in the CRC */
+ if (w->crc_active)
+ w->crc = ccitt_crc16_one(w->crc, data);
+ break;
+ default:
+ /* Byte already written. */
+ if (w->crc_active)
+ w->crc = ccitt_crc16_one(w->crc, data);
+ }
+ }
+ }
+ /* yes... setup a timed data request */
+ wd17xx_timed_data_request(device);
+ }
+ }
+ else
+ {
+ if (VERBOSE)
+ logerror("%s: wd17xx_data_w $%02X\n", device->machine().describe_context(), data);
+ }
+ w->data = data;
+}
+
+READ8_DEVICE_HANDLER( wd17xx_r )
+{
+ UINT8 data = 0;
+
+ switch (offset & 0x03)
+ {
+ case 0: data = wd17xx_status_r(device, 0); break;
+ case 1: data = wd17xx_track_r(device, 0); break;
+ case 2: data = wd17xx_sector_r(device, 0); break;
+ case 3: data = wd17xx_data_r(device, 0); break;
+ }
+
+ return data;
+}
+
+WRITE8_DEVICE_HANDLER( wd17xx_w )
+{
+ switch (offset & 0x03)
+ {
+ case 0: wd17xx_command_w(device, 0, data); break;
+ case 1: wd17xx_track_w(device, 0, data); break;
+ case 2: wd17xx_sector_w(device, 0, data); break;
+ case 3: wd17xx_data_w(device, 0, data); break;
+ }
+}
+
+
+/***************************************************************************
+ MAME DEVICE INTERFACE
+***************************************************************************/
+
+static DEVICE_START( wd1770 )
+{
+ wd1770_state *w = get_safe_token(device);
+
+ assert(device->static_config() != NULL);
+
+ w->intf = (const wd17xx_interface*)device->static_config();
+
+ w->status = STA_1_TRACK0;
+ w->pause_time = 1000;
+
+ /* allocate timers */
+ w->timer_cmd = device->machine().scheduler().timer_alloc(FUNC(wd17xx_command_callback), (void *)device);
+ w->timer_data = device->machine().scheduler().timer_alloc(FUNC(wd17xx_data_callback), (void *)device);
+ w->timer_rs = device->machine().scheduler().timer_alloc(FUNC(wd17xx_read_sector_callback), (void *)device);
+ w->timer_ws = device->machine().scheduler().timer_alloc(FUNC(wd17xx_write_sector_callback), (void *)device);
+
+ /* resolve callbacks */
+ w->in_dden_func.resolve(w->intf->in_dden_func, *device);
+ w->out_intrq_func.resolve(w->intf->out_intrq_func, *device);
+ w->out_drq_func.resolve(w->intf->out_drq_func, *device);
+
+ /* stepping rate depends on the clock */
+ w->stepping_rate[0] = 6;
+ w->stepping_rate[1] = 12;
+ w->stepping_rate[2] = 20;
+ w->stepping_rate[3] = 30;
+}
+
+static DEVICE_START( wd1772 )
+{
+ wd1770_state *w = get_safe_token(device);
+
+ DEVICE_START_CALL(wd1770);
+
+ /* the 1772 has faster track stepping rates */
+ w->stepping_rate[0] = 6;
+ w->stepping_rate[1] = 12;
+ w->stepping_rate[2] = 2;
+ w->stepping_rate[3] = 3;
+}
+
+static DEVICE_RESET( wd1770 )
+{
+ wd1770_state *w = get_safe_token(device);
+ int i;
+
+ /* set the default state of some input lines */
+ w->mr = ASSERT_LINE;
+ w->wprt = ASSERT_LINE;
+ w->dden = ASSERT_LINE;
+
+ for (i = 0; i < 4; i++)
+ {
+ if(w->intf->floppy_drive_tags[i]!=NULL) {
+ device_t *img = NULL;
+
+ if (device->owner() != NULL)
+ img = device->owner()->subdevice(w->intf->floppy_drive_tags[i]);
+ if (img == NULL) {
+ img = device->machine().device(w->intf->floppy_drive_tags[i]);
+ }
+
+ else
+ img = device->machine().device(w->intf->floppy_drive_tags[i]);
+
+ if (img!=NULL) {
+ floppy_drive_set_controller(img,device);
+ floppy_drive_set_index_pulse_callback(img, wd17xx_index_pulse_callback);
+ floppy_drive_set_rpm( img, 300.);
+ }
+ }
+ }
+
+ wd17xx_set_drive(device, 0);
+
+ w->hd = 0;
+ w->hld_count = 0;
+ w->sector = 1;
+ wd17xx_command_restore(device);
+}
+
+void wd17xx_reset(device_t *device)
+{
+ DEVICE_RESET_CALL( wd1770 );
+}
+
+
+/***************************************************************************
+ DEVICE GETINFO
+***************************************************************************/
+
+static const char DEVTEMPLATE_SOURCE[] = __FILE__;
+
+#define DEVTEMPLATE_ID(p,s) p##wd1770##s
+#define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_RESET
+#define DEVTEMPLATE_NAME "WD1770"
+#define DEVTEMPLATE_FAMILY "WD17xx"
+#define DEVTEMPLATE_VERSION "1.0"
+#define DEVTEMPLATE_CREDITS "Copyright MESS Team"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1771##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1771"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1781##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1781"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1791##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1791"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1792##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1792"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1793##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1793"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1794##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1794"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1795##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1795"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1797##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1797"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1761##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1761"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1762##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1762"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1763##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1763"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1764##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1764"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1765##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1765"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##fd1767##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "FD1767"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##wd2791##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "WD2791"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##wd2793##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "WD2793"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##wd2795##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "WD2795"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##wd2797##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "WD2797"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##wd1772##s
+#define DEVTEMPLATE_DERIVED_FEATURES DT_HAS_START
+#define DEVTEMPLATE_DERIVED_NAME "WD1772"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##wd1773##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "WD1773"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##mb8866##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "MB8866"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##mb8876##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "MB8876"
+#include "devtempl.h"
+
+#define DEVTEMPLATE_DERIVED_ID(p,s) p##mb8877##s
+#define DEVTEMPLATE_DERIVED_FEATURES 0
+#define DEVTEMPLATE_DERIVED_NAME "MB8877"
+#include "devtempl.h"
+
+DEFINE_LEGACY_DEVICE(FD1771, fd1771);
+DEFINE_LEGACY_DEVICE(FD1781, fd1781);
+DEFINE_LEGACY_DEVICE(FD1791, fd1791);
+DEFINE_LEGACY_DEVICE(FD1792, fd1792);
+DEFINE_LEGACY_DEVICE(FD1793, fd1793);
+DEFINE_LEGACY_DEVICE(FD1794, fd1794);
+DEFINE_LEGACY_DEVICE(FD1795, fd1795);
+DEFINE_LEGACY_DEVICE(FD1797, fd1797);
+DEFINE_LEGACY_DEVICE(FD1761, fd1761);
+DEFINE_LEGACY_DEVICE(FD1762, fd1762);
+DEFINE_LEGACY_DEVICE(FD1763, fd1763);
+DEFINE_LEGACY_DEVICE(FD1764, fd1764);
+DEFINE_LEGACY_DEVICE(FD1765, fd1765);
+DEFINE_LEGACY_DEVICE(FD1767, fd1767);
+DEFINE_LEGACY_DEVICE(WD2791, wd2791);
+DEFINE_LEGACY_DEVICE(WD2793, wd2793);
+DEFINE_LEGACY_DEVICE(WD2795, wd2795);
+DEFINE_LEGACY_DEVICE(WD2797, wd2797);
+DEFINE_LEGACY_DEVICE(WD1770, wd1770);
+DEFINE_LEGACY_DEVICE(WD1772, wd1772);
+DEFINE_LEGACY_DEVICE(WD1773, wd1773);
+DEFINE_LEGACY_DEVICE(MB8866, mb8866);
+DEFINE_LEGACY_DEVICE(MB8876, mb8876);
+DEFINE_LEGACY_DEVICE(MB8877, mb8877);