diff options
Diffstat (limited to 'trunk/src/emu/machine/wd17xx.c')
-rw-r--r-- | trunk/src/emu/machine/wd17xx.c | 2255 |
1 files changed, 2255 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..b0d19483609 --- /dev/null +++ b/trunk/src/emu/machine/wd17xx.c @@ -0,0 +1,2255 @@ +/*************************************************************************** + + 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. + + 2011-Sep-18 Curt Coder + - Connected Side Select Output for variants that support it. + + 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; +} + +static void wd17xx_side_compare(device_t *device, UINT8 command) +{ + wd1770_state *w = get_safe_token(device); + + if (wd17xx_has_side_select(device)) + wd17xx_set_side(device, (command & FDC_SIDE_CMP_T) ? 1 : 0); + + if (command & FDC_SIDE_CMP_T) + w->head = (command & FDC_SIDE_CMP_S) ? 1 : 0; + else + w->head = ~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? */ + wd17xx_side_compare(device, w->read_cmd); + + 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? */ + wd17xx_side_compare(device, w->write_cmd); + + /* 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? */ + wd17xx_side_compare(device, w->write_cmd); + + /* 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) + { + w->drive = device->siblingdevice(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; + + img = device->siblingdevice(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); |