1 /*************************************************************************
2  *                                                                       *
3  * $Id: wd179x.c 1999 2008-07-22 04:25:28Z hharte $                      *
4  *                                                                       *
5  * Copyright (c) 2007-2008 Howard M. Harte.                              *
6  * http://www.hartetec.com                                               *
7  *                                                                       *
8  * Permission is hereby granted, free of charge, to any person obtaining *
9  * a copy of this software and associated documentation files (the       *
10  * "Software"), to deal in the Software without restriction, including   *
11  * without limitation the rights to use, copy, modify, merge, publish,   *
12  * distribute, sublicense, and/or sell copies of the Software, and to    *
13  * permit persons to whom the Software is furnished to do so, subject to *
14  * the following conditions:                                             *
15  *                                                                       *
16  * The above copyright notice and this permission notice shall be        *
17  * included in all copies or substantial portions of the Software.       *
18  *                                                                       *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                 *
22  * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY  *
23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  *
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     *
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                *
26  *                                                                       *
27  * Except as contained in this notice, the name of Howard M. Harte shall *
28  * not be used in advertising or otherwise to promote the sale, use or   *
29  * other dealings in this Software without prior written authorization   *
30  * Howard M. Harte.                                                      *
31  *                                                                       *
32  * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn.            *
33  *                                                                       *
34  * Module Description:                                                   *
35  *     Generic WD179X Disk Controller module for SIMH.                   *
36  *                                                                       *
37  * Environment:                                                          *
38  *     User mode only                                                    *
39  *                                                                       *
40  *************************************************************************/
41 
42 /*#define DBG_MSG */
43 
44 #include "altairz80_defs.h"
45 
46 #if defined (_WIN32)
47 #include <windows.h>
48 #endif
49 
50 #include "sim_imd.h"
51 #include "wd179x.h"
52 
53 #ifdef DBG_MSG
54 #define DBG_PRINT(args) printf args
55 #else
56 #define DBG_PRINT(args)
57 #endif
58 
59 #define CROMFDC_SIM_100US   291     /* Number of "ticks" in 100uS, where does this come from? */
60 #define CROMFDC_8IN_ROT     (167 * CROMFDC_SIM_100US)
61 #define CROMFDC_5IN_ROT     (200 * CROMFDC_SIM_100US)
62 
63 /* Debug flags */
64 #define ERROR_MSG   (1 << 0)
65 #define SEEK_MSG    (1 << 1)
66 #define CMD_MSG     (1 << 2)
67 #define RD_DATA_MSG (1 << 3)
68 #define WR_DATA_MSG (1 << 4)
69 #define STATUS_MSG  (1 << 5)
70 #define FMT_MSG     (1 << 6)
71 #define VERBOSE_MSG (1 << 7)
72 
73 #define WD179X_MAX_DRIVES   4
74 #define WD179X_SECTOR_LEN   8192
75 /* 2^(7 + WD179X_MAX_SEC_LEN) == WD179X_SECTOR_LEN */
76 #define WD179X_MAX_SEC_LEN  6
77 #define WD179X_MAX_SECTOR   26
78 
79 #define CMD_PHASE 0
80 #define EXEC_PHASE 1
81 #define DATA_PHASE 2
82 
83 /* Status Bits for Type I Commands */
84 #define WD179X_STAT_NOT_READY   (1 << 7)
85 #define WD179X_STAT_WPROT       (1 << 6)
86 #define WD179X_STAT_HLD         (1 << 5)
87 #define WD179X_STAT_SEEK_ERROR  (1 << 4)
88 #define WD179X_STAT_CRC_ERROR   (1 << 3)
89 #define WD179X_STAT_TRACK0      (1 << 2)
90 #define WD179X_STAT_INDEX       (1 << 1)
91 #define WD179X_STAT_BUSY        (1 << 0)
92 
93 /* Status Bits for Type II, III Commands */
94 /*#define WD179X_STAT_NOT_READY (1 << 7) */
95 /*#define WD179X_STAT_WPROT     (1 << 6) */
96 #define WD179X_STAT_REC_TYPE    (1 << 5)    /* Also Write Fault */
97 #define WD179X_STAT_NOT_FOUND   (1 << 4)
98 /*#define WD179X_STAT_CRC_ERROR (1 << 3) */
99 #define WD179X_STAT_LOST_DATA   (1 << 2)
100 #define WD179X_STAT_DRQ         (1 << 1)
101 /*#define WD179X_STAT_BUSY      (1 << 0) */
102 
103 typedef union {
104     uint8 raw[WD179X_SECTOR_LEN];
105 } SECTOR_FORMAT;
106 
107 typedef struct {
108     UNIT *uptr;
109     DISK_INFO *imd;
110     uint8 ntracks;   /* number of tracks */
111     uint8 nheads;    /* number of heads */
112     uint32 sectsize; /* sector size, not including pre/postamble */
113     uint8 track;     /* Current Track */
114     uint8 ready;     /* Is drive ready? */
115 } WD179X_DRIVE_INFO;
116 
117 typedef struct {
118     PNP_INFO pnp;       /* Plug-n-Play Information */
119     uint8 intrq;        /* WD179X Interrupt Request Output (EOJ) */
120     uint8 hld;          /* WD179X Head Load Output */
121     uint8 drq;          /* WD179X DMA Request Output */
122     uint8 ddens;        /* WD179X Double-Density Input */
123     uint8 fdc_head;     /* H Head Number */
124     uint8 sel_drive;    /* Currently selected drive */
125     uint8 drivetype;    /* 8 or 5 depending on disk type. */
126     uint8 fdc_status;   /* WD179X Status Register */
127     uint8 verify;       /* WD179X Type 1 command Verify flag */
128     uint8 fdc_data;     /* WD179X Data Register */
129     uint8 fdc_read;     /* TRUE when reading */
130     uint8 fdc_write;    /* TRUE when writing */
131     uint8 fdc_write_track;  /* TRUE when writing an entire track */
132     uint8 fdc_fmt_state;    /* Format track statemachine state */
133     uint8 fdc_gap[4];   /* Gap I - Gap IV lengths */
134     uint8 fdc_fmt_sector_count; /* sector count for format track */
135     uint8 fdc_sectormap[WD179X_MAX_SECTOR]; /* Physical to logical sector map */
136     uint8 fdc_header_index; /* Index into header */
137     uint8 fdc_read_addr;    /* TRUE when READ ADDRESS command is in progress */
138     uint8 fdc_multiple; /* TRUE for multi-sector read/write */
139     uint16 fdc_datacount;   /* Read or Write data remaining transfer length */
140     uint16 fdc_dataindex;   /* index of current byte in sector data */
141     uint8 index_pulse_wait; /* TRUE if waiting for interrupt on next index pulse. */
142     uint8 fdc_sector;   /* R Record (Sector) */
143     uint8 fdc_sec_len;  /* N Sector Length */
144     int8 step_dir;
145     uint8 cmdtype;      /* Type of current/former command */
146     WD179X_DRIVE_INFO drive[WD179X_MAX_DRIVES];
147 } WD179X_INFO;
148 
149 static SECTOR_FORMAT sdata;
150 extern uint32 PCX;
151 extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);
152 extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc);
153 extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
154         int32 (*routine)(const int32, const int32, const int32), uint8 unmap);
155 extern int32 find_unit_index (UNIT *uptr);
156 
157 t_stat wd179x_svc (UNIT *uptr);
158 
159 /* These are needed for DMA.  PIO Mode has not been implemented yet. */
160 extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value);
161 extern uint8 GetBYTEWrapper(const uint32 Addr);
162 
163 #define UNIT_V_WD179X_WLK        (UNIT_V_UF + 0) /* write locked                             */
164 #define UNIT_WD179X_WLK          (1 << UNIT_V_WD179X_WLK)
165 #define UNIT_V_WD179X_VERBOSE    (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages   */
166 #define UNIT_WD179X_VERBOSE      (1 << UNIT_V_WD179X_VERBOSE)
167 #define WD179X_CAPACITY          (77*2*16*256)   /* Default Micropolis Disk Capacity         */
168 #define WD179X_CAPACITY_SSSD     (77*1*26*128)   /* Single-sided Single Density IBM Diskette1 */
169 
170 /* Write Track (format) Statemachine states */
171 #define FMT_GAP1    1
172 #define FMT_GAP2    2
173 #define FMT_GAP3    3
174 #define FMT_GAP4    4
175 #define FMT_HEADER  5
176 #define FMT_DATA    6
177 
178 /* WD179X Commands */
179 #define WD179X_RESTORE               0x00   /* Type I */
180 #define WD179X_SEEK                  0x10   /* Type I */
181 #define WD179X_STEP                  0x20   /* Type I */
182 #define WD179X_STEP_U                0x30   /* Type I */
183 #define WD179X_STEP_IN               0x40   /* Type I */
184 #define WD179X_STEP_IN_U             0x50   /* Type I */
185 #define WD179X_STEP_OUT              0x60   /* Type I */
186 #define WD179X_STEP_OUT_U            0x70   /* Type I */
187 #define WD179X_READ_REC              0x80   /* Type II */
188 #define WD179X_READ_RECS             0x90   /* Type II */
189 #define WD179X_WRITE_REC             0xA0   /* Type II */
190 #define WD179X_WRITE_RECS            0xB0   /* Type II */
191 #define WD179X_READ_ADDR             0xC0   /* Type III */
192 #define WD179X_FORCE_INTR            0xD0   /* Type IV */
193 #define WD179X_READ_TRACK            0xE0   /* Type III */
194 #define WD179X_WRITE_TRACK           0xF0   /* Type III */
195 
196 static int32 wd179xdev(const int32 port, const int32 io, const int32 data);
197 static t_stat wd179x_reset(DEVICE *dptr);
198 uint8 floorlog2(unsigned int n);
199 
200 WD179X_INFO wd179x_info_data = { { 0x0, 0, 0x30, 4 } };
201 WD179X_INFO *wd179x_info = &wd179x_info_data;
202 WD179X_INFO_PUB *wd179x_infop = (WD179X_INFO_PUB *)&wd179x_info_data;
203 
204 static UNIT wd179x_unit[] = {
205     { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 },
206     { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 },
207     { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 },
208     { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 }
209 };
210 
211 static MTAB wd179x_mod[] = {
212     { MTAB_XTD|MTAB_VDV,    0,                      "IOBASE",   "IOBASE",   &set_iobase, &show_iobase, NULL },
213     { UNIT_WD179X_WLK,      0,                      "WRTENB",   "WRTENB",   NULL },
214     { UNIT_WD179X_WLK,      UNIT_WD179X_WLK,        "WRTLCK",   "WRTLCK",   NULL },
215     /* quiet, no warning messages       */
216     { UNIT_WD179X_VERBOSE,  0,                      "QUIET",    "QUIET",    NULL },
217     /* verbose, show warning messages   */
218     { UNIT_WD179X_VERBOSE,  UNIT_WD179X_VERBOSE,    "VERBOSE",  "VERBOSE",  NULL },
219     { 0 }
220 };
221 
222 /* Debug Flags */
223 static DEBTAB wd179x_dt[] = {
224     { "ERROR",  ERROR_MSG },
225     { "SEEK",   SEEK_MSG },
226     { "CMD",    CMD_MSG },
227     { "RDDATA", RD_DATA_MSG },
228     { "WRDATA", WR_DATA_MSG },
229     { "STATUS", STATUS_MSG },
230     { "FMT",    FMT_MSG },
231     { "VERBOSE",VERBOSE_MSG },
232     { NULL,     0 }
233 };
234 
235 DEVICE wd179x_dev = {
236     "WD179X", wd179x_unit, NULL, wd179x_mod,
237     WD179X_MAX_DRIVES, 10, 31, 1, WD179X_MAX_DRIVES, WD179X_MAX_DRIVES,
238     NULL, NULL, &wd179x_reset,
239     NULL, &wd179x_attach, &wd179x_detach,
240     &wd179x_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG,
241     wd179x_dt, NULL, "Western Digital FDC Core WD179X"
242 };
243 
244 /* Unit service routine */
245 /* Used to generate INDEX pulses in response to a FORCE_INTR command */
wd179x_svc(UNIT * uptr)246 t_stat wd179x_svc (UNIT *uptr)
247 {
248 
249     if(wd179x_info->index_pulse_wait == TRUE) {
250         wd179x_info->index_pulse_wait = FALSE;
251         wd179x_info->intrq = 1;
252     }
253 
254     return SCPE_OK;
255 }
256 
257 
258 /* Reset routine */
wd179x_reset(DEVICE * dptr)259 static t_stat wd179x_reset(DEVICE *dptr)
260 {
261     PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
262 
263     if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */
264         sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, TRUE);
265     } else {
266         /* Connect I/O Ports at base address */
267         if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, FALSE) != 0) {
268             printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base);
269             return SCPE_ARG;
270         }
271     }
272     return SCPE_OK;
273 }
274 
wd179x_external_restore(void)275 void wd179x_external_restore(void)
276 {
277     WD179X_DRIVE_INFO    *pDrive;
278 
279     if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) {
280         sim_debug(ERROR_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
281                   " Illegal drive selected, cannot restore.\n", PCX);
282         return;
283     }
284 
285     pDrive = &wd179x_info->drive[wd179x_info->sel_drive];
286 
287     if(pDrive->uptr == NULL) {
288         sim_debug(ERROR_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
289                   " No drive selected, cannot restore.\n", PCX);
290         return;
291     }
292 
293     sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
294               " External Restore drive to track 0\n", wd179x_info->sel_drive, PCX);
295 
296     pDrive->track = 0;
297 
298 }
299 
300 /* Attach routine */
wd179x_attach(UNIT * uptr,char * cptr)301 t_stat wd179x_attach(UNIT *uptr, char *cptr)
302 {
303     char header[4];
304     t_stat r;
305     int32 i = 0;
306 
307     r = attach_unit(uptr, cptr);    /* attach unit  */
308     if ( r != SCPE_OK)              /* error?       */
309         return r;
310 
311     /* Determine length of this disk */
312     uptr->capac = sim_fsize(uptr->fileref);
313 
314     i = find_unit_index(uptr);
315 
316     if (i == -1) {
317         return (SCPE_IERR);
318     }
319 
320     DBG_PRINT(("Attach WD179X%d\n", i));
321     wd179x_info->drive[i].uptr = uptr;
322 
323     /* Default to drive not ready */
324     wd179x_info->drive[i].ready = 0;
325 
326     if(uptr->capac > 0) {
327         char *rtn = fgets(header, 4, uptr->fileref);
328         if ((rtn != NULL) && strncmp(header, "IMD", 3)) {
329             printf("WD179X: Only IMD disk images are supported\n");
330             wd179x_info->drive[i].uptr = NULL;
331             return SCPE_OPENERR;
332         }
333     } else {
334         /* create a disk image file in IMD format. */
335         if (diskCreate(uptr->fileref, "$Id: wd179x.c 1999 2008-07-22 04:25:28Z hharte $") != SCPE_OK) {
336             printf("WD179X: Failed to create IMD disk.\n");
337             wd179x_info->drive[i].uptr = NULL;
338             return SCPE_OPENERR;
339         }
340         uptr->capac = sim_fsize(uptr->fileref);
341     }
342 
343     uptr->u3 = IMAGE_TYPE_IMD;
344 
345     if (uptr->flags & UNIT_WD179X_VERBOSE)
346         printf("WD179X%d: attached to '%s', type=%s, len=%d\n", i, cptr,
347             uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK",
348             uptr->capac);
349 
350     if(uptr->u3 == IMAGE_TYPE_IMD) {
351         if (uptr->flags & UNIT_WD179X_VERBOSE)
352             printf("--------------------------------------------------------\n");
353         wd179x_info->drive[i].imd = diskOpen(uptr->fileref, uptr->flags & UNIT_WD179X_VERBOSE);
354         if (uptr->flags & UNIT_WD179X_VERBOSE)
355             printf("\n");
356         if (wd179x_info->drive[i].imd == NULL) {
357             printf("WD179X: IMD disk corrupt.\n");
358             wd179x_info->drive[i].uptr = NULL;
359             return SCPE_OPENERR;
360         }
361 
362         /* Write-protect the unit if IMD think's it's writelocked. */
363         if(imdIsWriteLocked(wd179x_info->drive[i].imd)) {
364             uptr->flags |= UNIT_WD179X_WLK;
365         }
366 
367         wd179x_info->drive[i].ready = 1;
368     } else {
369         wd179x_info->drive[i].imd = NULL;
370     }
371 
372     wd179x_info->fdc_sec_len = 0; /* 128 byte sectors, fixme */
373     wd179x_info->sel_drive = 0;
374 
375     return SCPE_OK;
376 }
377 
378 
379 /* Detach routine */
wd179x_detach(UNIT * uptr)380 t_stat wd179x_detach(UNIT *uptr)
381 {
382     t_stat r;
383     int8 i;
384 
385     i = find_unit_index(uptr);
386 
387     if (i == -1) {
388         return SCPE_IERR;
389     }
390 
391     DBG_PRINT(("Detach WD179X%d\n", i));
392     r = diskClose(&wd179x_info->drive[i].imd);
393     wd179x_info->drive[i].ready = 0;
394     if (r != SCPE_OK)
395         return r;
396 
397     r = detach_unit(uptr);  /* detach unit */
398     if (r != SCPE_OK)
399         return r;
400 
401     return SCPE_OK;
402 }
403 
404 
wd179xdev(const int32 port,const int32 io,const int32 data)405 static int32 wd179xdev(const int32 port, const int32 io, const int32 data)
406 {
407     DBG_PRINT(("WD179X: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x" NLP,
408         PCX, io ? "OUT" : " IN", port, data));
409     if(io) {
410         WD179X_Write(port, data);
411         return 0;
412     } else {
413         return(WD179X_Read(port));
414     }
415 }
416 
floorlog2(unsigned int n)417 uint8 floorlog2(unsigned int n)
418 {
419     /* Compute log2(n) */
420     uint8 r = 0;
421     if (n >= 1<<16) {
422         n >>=16;
423         r += 16;
424     }
425     if (n >= 1<< 8) {
426         n >>= 8;
427         r +=  8;
428     }
429     if (n >= 1<< 4) {
430         n >>= 4;
431         r +=  4;
432     }
433     if (n >= 1<< 2) {
434         n >>= 2;
435         r +=  2;
436     }
437     if (n >= 1<< 1) {
438         r +=  1;
439     }
440     return ((n == 0) ? (0xFF) : r); /* 0xFF is error return value */
441 }
442 
WD179X_Read(const uint32 Addr)443 uint8 WD179X_Read(const uint32 Addr)
444 {
445     uint8 cData;
446     WD179X_DRIVE_INFO    *pDrive;
447     uint32 flags = 0;
448     uint32 readlen;
449     int status;
450 
451     if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) {
452         return 0xFF;
453     }
454 
455     pDrive = &wd179x_info->drive[wd179x_info->sel_drive];
456 
457     if(pDrive->uptr == NULL) {
458         return 0xFF;
459     }
460 
461     cData = 0x00;
462 
463     switch(Addr & 0x3) {
464         case WD179X_STATUS:
465             /* Fix up status based on Command Type */
466             if((wd179x_info->cmdtype == 1) || (wd179x_info->cmdtype == 4)) {
467                 wd179x_info->fdc_status ^= WD179X_STAT_INDEX;   /* Generate Index pulses */
468                 wd179x_info->fdc_status &= ~WD179X_STAT_TRACK0;
469                 wd179x_info->fdc_status |= (pDrive->track == 0) ? WD179X_STAT_TRACK0 : 0;
470             } else if(wd179x_info->cmdtype == 4) {
471             }
472             else {
473                 wd179x_info->fdc_status &= ~WD179X_STAT_INDEX;  /* Mask index pulses */
474                 wd179x_info->fdc_status |= (wd179x_info->drq) ? WD179X_STAT_DRQ : 0;
475             }
476             cData = (pDrive->ready == 0) ? WD179X_STAT_NOT_READY : 0;
477             cData |= wd179x_info->fdc_status;   /* Status Register */
478             sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
479                       " RD STATUS = 0x%02x\n", PCX, cData);
480             wd179x_info->intrq = 0;
481             break;
482         case WD179X_TRACK:
483             cData = pDrive->track;
484             sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
485                       " RD TRACK = 0x%02x\n", PCX, cData);
486             break;
487         case WD179X_SECTOR:
488             cData = wd179x_info->fdc_sector;
489             sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
490                       " RD SECT  = 0x%02x\n", PCX, cData);
491             break;
492         case WD179X_DATA:
493             cData = 0xFF;      /* Return High-Z data */
494             if(wd179x_info->fdc_read == TRUE) {
495                 if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) {
496                     cData = sdata.raw[wd179x_info->fdc_dataindex];
497                     if(wd179x_info->fdc_read_addr == TRUE) {
498                         sim_debug(STATUS_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
499                                   " READ_ADDR[%d] = 0x%02x\n",
500                                   wd179x_info->sel_drive, PCX,
501                                   wd179x_info->fdc_dataindex, cData);
502                     }
503 
504                     wd179x_info->fdc_dataindex++;
505                     if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) {
506                         if(wd179x_info->fdc_multiple == FALSE) {
507                             wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY);       /* Clear DRQ, BUSY */
508                             wd179x_info->drq = 0;
509                             wd179x_info->intrq = 1;
510                             wd179x_info->fdc_read = FALSE;
511                             wd179x_info->fdc_read_addr = FALSE;
512                         } else {
513 
514                             /* Compute Sector Size */
515                             wd179x_info->fdc_sec_len = floorlog2(
516                                 pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7;
517                             if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */
518                                 sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!\n", wd179x_info->sel_drive, PCX);
519                                 wd179x_info->fdc_sec_len = 0;
520                                 return cData;
521                             }
522 
523                             wd179x_info->fdc_sector ++;
524                             sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " MULTI_READ_REC, T:%d/S:%d/N:%d, %s, len=%d\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, wd179x_info->ddens ? "DD" : "SD", 128 << wd179x_info->fdc_sec_len);
525 
526                             status = sectRead(pDrive->imd,
527                                 pDrive->track,
528                                 wd179x_info->fdc_head,
529                                 wd179x_info->fdc_sector,
530                                 sdata.raw,
531                                 128 << wd179x_info->fdc_sec_len,
532                                 &flags,
533                                 &readlen);
534 
535                             if(status == SCPE_OK) {
536                                 wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY);     /* Set DRQ, BUSY */
537                                 wd179x_info->drq = 1;
538                                 wd179x_info->intrq = 0;
539                                 wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len;
540                                 wd179x_info->fdc_dataindex = 0;
541                                 wd179x_info->fdc_read = TRUE;
542                                 wd179x_info->fdc_read_addr = FALSE;
543                             } else {
544                                 wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */
545                                 wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;
546                                 wd179x_info->drq = 0;
547                                 wd179x_info->intrq = 1;
548                                 wd179x_info->fdc_read = FALSE;
549                                 wd179x_info->fdc_read_addr = FALSE;
550                             }
551                         }
552                     }
553                 }
554             }
555             break;
556     }
557 
558     return (cData);
559 }
560 
561 /*
562  * Command processing happens in three stages:
563  * 1. Flags and initial conditions are set up based on the Type of the command.
564  * 2. The execution phase takes place.
565  * 3. Status is updated based on the Type and outcome of the command execution.
566  *
567  * See the WD179x-02 Datasheet available on www.hartetechnologies.com/manuals/
568  *
569  */
Do1793Command(uint8 cCommand)570 static uint8 Do1793Command(uint8 cCommand)
571 {
572     uint8 result = 0;
573     WD179X_DRIVE_INFO    *pDrive;
574     uint32 flags = 0;
575     uint32 readlen;
576     int status;
577 
578     if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) {
579         return 0xFF;
580     }
581 
582     pDrive = &wd179x_info->drive[wd179x_info->sel_drive];
583 
584     if(pDrive->uptr == NULL) {
585         return 0xFF;
586     }
587 
588     if(wd179x_info->fdc_status & WD179X_STAT_BUSY) {
589         if(((cCommand & 0xF0) != WD179X_FORCE_INTR)) { /* && ((cCommand & 0xF0) != WD179X_RESTORE)) { */
590             sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
591                       " ERROR: Command 0x%02x ignored because controller is BUSY\n\n",
592                       wd179x_info->sel_drive, PCX, cCommand);
593         }
594         return 0xFF;
595     }
596 
597     wd179x_info->fdc_status &= ~WD179X_STAT_NOT_READY;
598 
599     /* Extract Type-specific command flags, and set initial conditions */
600     switch(cCommand & 0xF0) {
601         /* Type I Commands */
602         case WD179X_RESTORE:
603         case WD179X_SEEK:
604         case WD179X_STEP:
605         case WD179X_STEP_U:
606         case WD179X_STEP_IN:
607         case WD179X_STEP_IN_U:
608         case WD179X_STEP_OUT:
609         case WD179X_STEP_OUT_U:
610             wd179x_info->cmdtype = 1;
611             wd179x_info->fdc_status |= WD179X_STAT_BUSY;        /* Set BUSY */
612             wd179x_info->fdc_status &= ~(WD179X_STAT_CRC_ERROR | WD179X_STAT_SEEK_ERROR | WD179X_STAT_DRQ);
613             wd179x_info->intrq = 0;
614             wd179x_info->hld = cCommand & 0x08;
615             wd179x_info->verify = cCommand & 0x04;
616             break;
617         /* Type II Commands */
618         case WD179X_READ_REC:
619         case WD179X_READ_RECS:
620         case WD179X_WRITE_REC:
621         case WD179X_WRITE_RECS:
622             wd179x_info->cmdtype = 2;
623             wd179x_info->fdc_status = WD179X_STAT_BUSY;     /* Set BUSY, clear all others */
624             wd179x_info->intrq = 0;
625             wd179x_info->hld = 1;   /* Load the head immediately, E Flag not checked. */
626             break;
627         /* Type III Commands */
628         case WD179X_READ_ADDR:
629         case WD179X_READ_TRACK:
630         case WD179X_WRITE_TRACK:
631             wd179x_info->cmdtype = 3;
632             break;
633         /* Type IV Commands */
634         case WD179X_FORCE_INTR:
635             wd179x_info->cmdtype = 4;
636             break;
637         default:
638             wd179x_info->cmdtype = 0;
639             break;
640     }
641 
642     switch(cCommand & 0xF0) {
643         /* Type I Commands */
644         case WD179X_RESTORE:
645             sim_debug(CMD_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
646                       " CMD=RESTORE %s\n", wd179x_info->sel_drive, PCX,
647                       wd179x_info->verify ? "[VERIFY]" : "");
648             pDrive->track = 0;
649             wd179x_info->intrq = 1;
650             break;
651         case WD179X_SEEK:
652             sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
653                       " CMD=SEEK, track=%d, new=%d\n", wd179x_info->sel_drive,
654                       PCX, pDrive->track, wd179x_info->fdc_data);
655             pDrive->track = wd179x_info->fdc_data;
656             break;
657         case WD179X_STEP:
658             sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
659                       " CMD=STEP\n", wd179x_info->sel_drive, PCX);
660             break;
661         case WD179X_STEP_U:
662             sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
663                       " CMD=STEP_U dir=%d\n", wd179x_info->sel_drive,
664                       PCX, wd179x_info->step_dir);
665             if(wd179x_info->step_dir == 1) {
666                 if (pDrive->track < 255)
667                     pDrive->track++;
668             } else if (wd179x_info->step_dir == -1) {
669                 if (pDrive->track > 0)
670                     pDrive->track--;
671             } else {
672                 sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
673                           " ERROR: undefined direction for STEP\n",
674                           wd179x_info->sel_drive, PCX);
675             }
676             break;
677         case WD179X_STEP_IN:
678             sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
679                       " CMD=STEP_IN\n", wd179x_info->sel_drive, PCX);
680             break;
681         case WD179X_STEP_IN_U:
682             if (pDrive->track < 255)
683                 pDrive->track++;
684             wd179x_info->step_dir = 1;
685             sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
686                       " CMD=STEP_IN_U, Track=%d\n", wd179x_info->sel_drive,
687                       PCX, pDrive->track);
688             break;
689         case WD179X_STEP_OUT:
690             sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
691                       " CMD=STEP_OUT\n", wd179x_info->sel_drive, PCX);
692             break;
693         case WD179X_STEP_OUT_U:
694             sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
695                       " CMD=STEP_OUT_U\n", wd179x_info->sel_drive, PCX);
696             if (pDrive->track > 0)
697                 pDrive->track--;
698             wd179x_info->step_dir = -1;
699             break;
700         /* Type II Commands */
701         case WD179X_READ_REC:
702         case WD179X_READ_RECS:
703             /* Compute Sector Size */
704             wd179x_info->fdc_sec_len = floorlog2(
705                 pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7;
706             if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */
707                 sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
708                           " Invalid sector size!\n", wd179x_info->sel_drive, PCX);
709                 wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;       /* Sector not found */
710                 wd179x_info->fdc_status &= ~WD179X_STAT_BUSY;
711                 wd179x_info->intrq = 1;
712                 wd179x_info->drq = 0;
713                 wd179x_info->fdc_sec_len = 0;
714                 return 0xFF;
715             }
716 
717             wd179x_info->fdc_multiple = (cCommand & 0x10) ? TRUE : FALSE;
718             sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
719                       " CMD=READ_REC, T:%d/S:%d/N:%d, %s, %s len=%d\n",
720                       wd179x_info->sel_drive, PCX, pDrive->track,
721                       wd179x_info->fdc_head, wd179x_info->fdc_sector,
722                       wd179x_info->fdc_multiple ? "Multiple" : "Single",
723                       wd179x_info->ddens ? "DD" : "SD", 128 << wd179x_info->fdc_sec_len);
724 
725             if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) {
726                 wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;       /* Sector not found */
727                 wd179x_info->fdc_status &= ~WD179X_STAT_BUSY;
728                 wd179x_info->intrq = 1;
729                 wd179x_info->drq = 0;
730             } else {
731 
732                 status = sectRead(pDrive->imd,
733                     pDrive->track,
734                     wd179x_info->fdc_head,
735                     wd179x_info->fdc_sector,
736                     sdata.raw,
737                     128 << wd179x_info->fdc_sec_len,
738                     &flags,
739                     &readlen);
740 
741                     if(status == SCPE_OK) {
742                         wd179x_info->fdc_status |= (WD179X_STAT_DRQ);       /* Set DRQ */
743                         wd179x_info->drq = 1;
744                         wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len;
745                         wd179x_info->fdc_dataindex = 0;
746                         wd179x_info->fdc_write = FALSE;
747                         wd179x_info->fdc_write_track = FALSE;
748                         wd179x_info->fdc_read = TRUE;
749                         wd179x_info->fdc_read_addr = FALSE;
750                     } else {
751                         wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */
752                         wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;
753                         wd179x_info->fdc_status &= ~WD179X_STAT_BUSY;
754                         wd179x_info->drq = 0;
755                         wd179x_info->intrq = 1;
756                         wd179x_info->fdc_read = FALSE;
757                         wd179x_info->fdc_read_addr = FALSE;
758                     }
759             }
760             break;
761         case WD179X_WRITE_RECS:
762             sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
763                       " Error: WRITE_RECS not implemented.\n", wd179x_info->sel_drive, PCX);
764             break;
765         case WD179X_WRITE_REC:
766             /* Compute Sector Size */
767             wd179x_info->fdc_sec_len = floorlog2(
768                 pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7;
769             if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */
770                 sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
771                           " Invalid sector size!\n", wd179x_info->sel_drive, PCX);
772                 wd179x_info->fdc_sec_len = 0;
773             }
774 
775             sim_debug(WR_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
776                       " CMD=WRITE_REC, T:%d/S:%d/N:%d, %s.\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, (cCommand & 0x10) ? "Multiple" : "Single");
777             wd179x_info->fdc_status |= (WD179X_STAT_DRQ);       /* Set DRQ */
778             wd179x_info->drq = 1;
779             wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len;
780             wd179x_info->fdc_dataindex = 0;
781             wd179x_info->fdc_write = TRUE;
782             wd179x_info->fdc_write_track = FALSE;
783             wd179x_info->fdc_read = FALSE;
784             wd179x_info->fdc_read_addr = FALSE;
785 
786             sdata.raw[wd179x_info->fdc_dataindex] = wd179x_info->fdc_data;
787             break;
788         /* Type III Commands */
789         case WD179X_READ_ADDR:
790             sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
791                       " CMD=READ_ADDR, T:%d/S:%d, %s\n", wd179x_info->sel_drive,
792                       PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->ddens ? "DD" : "SD");
793 
794             /* For some reason 86-DOS tries to use this track, force it to 0.   Need to investigate this more. */
795             if (pDrive->track == 0xFF)
796                 pDrive->track=0;
797 
798             /* Compute Sector Size */
799             wd179x_info->fdc_sec_len = floorlog2(
800                 pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7;
801             if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */
802                 sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
803                           " Invalid sector size!\n", wd179x_info->sel_drive, PCX);
804                 wd179x_info->fdc_sec_len = 0;
805             }
806 
807             if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) {
808                 wd179x_info->fdc_status = WD179X_STAT_NOT_FOUND;        /* Sector not found */
809                 wd179x_info->intrq = 1;
810             } else {
811                 wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY);     /* Set DRQ, BUSY */
812                 wd179x_info->drq = 1;
813                 wd179x_info->fdc_datacount = 6;
814                 wd179x_info->fdc_dataindex = 0;
815                 wd179x_info->fdc_read = TRUE;
816                 wd179x_info->fdc_read_addr = TRUE;
817 
818                 sdata.raw[0] = pDrive->track;
819                 sdata.raw[1] = wd179x_info->fdc_head;
820                 sdata.raw[2] = wd179x_info->fdc_sector;
821                 sdata.raw[3] = wd179x_info->fdc_sec_len;
822                 sdata.raw[4] = 0xAA; /* CRC1 */
823                 sdata.raw[5] = 0x55; /* CRC2 */
824 
825                 wd179x_info->fdc_sector = pDrive->track;
826                 wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY);     /* Clear BUSY */
827                 wd179x_info->intrq = 1;
828             }
829             break;
830         case WD179X_READ_TRACK:
831             sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
832                       " CMD=READ_TRACK\n", wd179x_info->sel_drive, PCX);
833             sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
834                       " Error: READ_TRACK not implemented.\n", wd179x_info->sel_drive, PCX);
835             break;
836         case WD179X_WRITE_TRACK:
837             sim_debug(WR_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
838                       " CMD=WRITE_TRACK\n", wd179x_info->sel_drive, PCX);
839             sim_debug(FMT_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
840                       " CMD=WRITE_TRACK, T:%d/S:%d.\n", wd179x_info->sel_drive,
841                       PCX, pDrive->track, wd179x_info->fdc_head);
842             wd179x_info->fdc_status |= (WD179X_STAT_DRQ);       /* Set DRQ */
843             wd179x_info->drq = 1;
844             wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len;
845             wd179x_info->fdc_dataindex = 0;
846             wd179x_info->fdc_write = FALSE;
847             wd179x_info->fdc_write_track = TRUE;
848             wd179x_info->fdc_read = FALSE;
849             wd179x_info->fdc_read_addr = FALSE;
850             wd179x_info->fdc_fmt_state = FMT_GAP1;  /* TRUE when writing an entire track */
851             wd179x_info->fdc_fmt_sector_count = 0;
852 
853             break;
854         /* Type IV Commands */
855         case WD179X_FORCE_INTR:
856             sim_debug(CMD_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
857                       " CMD=FORCE_INTR\n", wd179x_info->sel_drive, PCX);
858             if((cCommand & 0x0F) == 0) { /* I0-I3 == 0, no intr, but clear BUSY and terminate command */
859                 wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY);       /* Clear DRQ, BUSY */
860                 wd179x_info->drq = 0;
861                 wd179x_info->fdc_write = FALSE;
862                 wd179x_info->fdc_read = FALSE;
863                 wd179x_info->fdc_write_track = FALSE;
864                 wd179x_info->fdc_read_addr = FALSE;
865                 wd179x_info->fdc_datacount = 0;
866                 wd179x_info->fdc_dataindex = 0;
867             } else {
868                 if(wd179x_info->fdc_status & WD179X_STAT_BUSY) { /* Force Interrupt when command is pending */
869                 } else { /* Command not pending, clear status */
870                     wd179x_info->fdc_status = 0;
871                 }
872 
873                 if(cCommand & 0x04) {
874                     wd179x_info->index_pulse_wait = TRUE;
875                     if(wd179x_info->sel_drive < WD179X_MAX_DRIVES) {
876                         sim_activate (wd179x_unit, ((wd179x_info->drive[wd179x_info->sel_drive].imd->ntracks % 77) == 0) ? CROMFDC_8IN_ROT : CROMFDC_5IN_ROT); /* Generate INDEX pulse */
877 /*                      printf("Drive %d Num tracks=%d\n", wd179x_info->sel_drive, wd179x_info->drive[wd179x_info->sel_drive].imd->ntracks); */
878                     }
879                 } else {
880                     wd179x_info->intrq = 1;
881                 }
882                 wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY);     /* Clear BUSY */
883             }
884             break;
885         default:
886             sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
887                       " ERROR: Unknown command 0x%02x.\n\n", wd179x_info->sel_drive, PCX, cCommand);
888             break;
889     }
890 
891     /* Post processing of Type-specific command */
892     switch(cCommand & 0xF0) {
893         /* Type I Commands */
894         case WD179X_RESTORE:
895         case WD179X_SEEK:
896         case WD179X_STEP:
897         case WD179X_STEP_U:
898         case WD179X_STEP_IN:
899         case WD179X_STEP_IN_U:
900         case WD179X_STEP_OUT:
901         case WD179X_STEP_OUT_U:
902             if(wd179x_info->verify) { /* Verify the selected track/head is ok. */
903                 sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
904                           " Verify ", wd179x_info->sel_drive, PCX);
905                 if(sectSeek(pDrive->imd, pDrive->track, wd179x_info->fdc_head) != SCPE_OK) {
906                     sim_debug(SEEK_MSG, &wd179x_dev, "FAILED\n");
907                     wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;
908                 } else  if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) {
909                     wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;       /* Sector not found */
910                     sim_debug(SEEK_MSG, &wd179x_dev, "NOT FOUND\n");
911                 } else {
912                     sim_debug(SEEK_MSG, &wd179x_dev, "Ok\n");
913                 }
914             }
915 
916             if(pDrive->track == 0) {
917                 wd179x_info->fdc_status |= WD179X_STAT_TRACK0;
918             } else {
919                 wd179x_info->fdc_status &= ~(WD179X_STAT_TRACK0);
920             }
921 
922             wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY);     /* Clear BUSY */
923             wd179x_info->intrq = 1;
924             break;
925         /* Type II Commands */
926         case WD179X_READ_REC:
927         case WD179X_READ_RECS:
928         case WD179X_WRITE_REC:
929         case WD179X_WRITE_RECS:
930         /* Type III Commands */
931         case WD179X_READ_ADDR:
932         case WD179X_READ_TRACK:
933         case WD179X_WRITE_TRACK:
934         /* Type IV Commands */
935         case WD179X_FORCE_INTR:
936         default:
937             break;
938     }
939 
940 
941     return result;
942 }
943 
944 /* Maximum number of sectors per track for format */
945 uint8 max_sectors_per_track[2][7] = {
946     /* 128, 256, 512, 1024, 2048, 4096, 8192 */
947     {   26,  15,  8,   4,    2,    1,    0  }, /* Single-density table */
948     {   26,  26, 15,   8,    4,    2,    1  }  /* Double-density table */
949 };
950 
WD179X_Write(const uint32 Addr,uint8 cData)951 uint8 WD179X_Write(const uint32 Addr, uint8 cData)
952 {
953     WD179X_DRIVE_INFO    *pDrive;
954 /*    uint8   disk_read = 0; */
955     uint32 flags = 0;
956     uint32 writelen;
957 
958     if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) {
959         return 0xFF;
960     }
961 
962     pDrive = &wd179x_info->drive[wd179x_info->sel_drive];
963 
964     if(pDrive->uptr == NULL) {
965         return 0xFF;
966     }
967 
968     switch(Addr & 0x3) {
969         case WD179X_STATUS:
970             sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
971                       " WR CMD   = 0x%02x\n", PCX, cData);
972             wd179x_info->fdc_read = FALSE;
973             wd179x_info->fdc_write = FALSE;
974             wd179x_info->fdc_write_track = FALSE;
975             wd179x_info->fdc_datacount = 0;
976             wd179x_info->fdc_dataindex = 0;
977 
978             Do1793Command(cData);
979             break;
980         case WD179X_TRACK:
981             sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
982                       " WR TRACK = 0x%02x\n", PCX, cData);
983                 pDrive->track = cData;
984             break;
985         case WD179X_SECTOR:     /* Sector Register */
986             sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
987                       " WR SECT  = 0x%02x\n", PCX, cData);
988                 wd179x_info->fdc_sector = cData;
989             break;
990         case WD179X_DATA:
991             sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
992                       " WR DATA  = 0x%02x\n", PCX, cData);
993             if(wd179x_info->fdc_write == TRUE) {
994                 if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) {
995                     sdata.raw[wd179x_info->fdc_dataindex] = cData;
996 
997                     wd179x_info->fdc_dataindex++;
998                     if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) {
999                         wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY);       /* Clear DRQ, BUSY */
1000                         wd179x_info->drq = 0;
1001                         wd179x_info->intrq = 1;
1002 
1003                     sim_debug(WR_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
1004                               " Writing sector, T:%d/S:%d/N:%d, Len=%d\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, 128 << wd179x_info->fdc_sec_len);
1005 
1006                     sectWrite(pDrive->imd,
1007                         pDrive->track,
1008                         wd179x_info->fdc_head,
1009                         wd179x_info->fdc_sector,
1010                         sdata.raw,
1011                         128 << wd179x_info->fdc_sec_len,
1012                         &flags,
1013                         &writelen);
1014 
1015                     wd179x_info->fdc_write = FALSE;
1016                     }
1017                 }
1018             }
1019 
1020             if(wd179x_info->fdc_write_track == TRUE) {
1021                     if(wd179x_info->fdc_fmt_state == FMT_GAP1) {
1022                         if(cData != 0xFC) {
1023                             wd179x_info->fdc_gap[0]++;
1024                         } else {
1025                             sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
1026                                       " FMT GAP1 Length = %d\n", PCX, wd179x_info->fdc_gap[0]);
1027                             wd179x_info->fdc_gap[1] = 0;
1028                             wd179x_info->fdc_fmt_state = FMT_GAP2;
1029                         }
1030                     } else if(wd179x_info->fdc_fmt_state == FMT_GAP2) {
1031                         if(cData != 0xFE) {
1032                             wd179x_info->fdc_gap[1]++;
1033                         } else {
1034                             sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
1035                                       " FMT GAP2 Length = %d\n", PCX, wd179x_info->fdc_gap[1]);
1036                             wd179x_info->fdc_gap[2] = 0;
1037                             wd179x_info->fdc_fmt_state = FMT_HEADER;
1038                             wd179x_info->fdc_header_index = 0;
1039                         }
1040                     } else if(wd179x_info->fdc_fmt_state == FMT_HEADER) {
1041                         if(wd179x_info->fdc_header_index == 5) {
1042                             wd179x_info->fdc_gap[2] = 0;
1043                             wd179x_info->fdc_fmt_state = FMT_GAP3;
1044                         } else {
1045                             sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
1046                                       " HEADER[%d]=%02x\n", PCX, wd179x_info->fdc_header_index, cData);
1047                             switch(wd179x_info->fdc_header_index) {
1048                             case 0:
1049                                 pDrive->track = cData;
1050                                 break;
1051                             case 1:
1052                                 wd179x_info->fdc_head = cData;
1053                                 break;
1054                             case 2:
1055                                 wd179x_info->fdc_sector = cData;
1056                                 break;
1057                             case 3:
1058                                 if(cData != 0x00) {
1059                                 }
1060                                 break;
1061                             case 4:
1062                                 if(cData != 0xF7) {
1063                                 }
1064                                 break;
1065                             }
1066                             wd179x_info->fdc_header_index++;
1067                         }
1068                     } else if(wd179x_info->fdc_fmt_state == FMT_GAP3) {
1069                         if(cData != 0xFB) {
1070                             wd179x_info->fdc_gap[2]++;
1071                         } else {
1072                             sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
1073                                       " FMT GAP3 Length = %d\n", PCX, wd179x_info->fdc_gap[2]);
1074                             wd179x_info->fdc_fmt_state = FMT_DATA;
1075                             wd179x_info->fdc_dataindex = 0;
1076                         }
1077                     } else if(wd179x_info->fdc_fmt_state == FMT_DATA) { /* data bytes */
1078                         if(cData != 0xF7) {
1079                             sdata.raw[wd179x_info->fdc_dataindex] = cData;
1080                             wd179x_info->fdc_dataindex++;
1081                         } else {
1082                             wd179x_info->fdc_sec_len = floorlog2(wd179x_info->fdc_dataindex) - 7;
1083                             if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */
1084                                 sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
1085                                           " Invalid sector size!\n", wd179x_info->sel_drive, PCX);
1086                                 wd179x_info->fdc_sec_len = 0;
1087                             }
1088                             if(wd179x_info->fdc_fmt_sector_count >= WD179X_MAX_SECTOR) {
1089                                 sim_debug(ERROR_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
1090                                           " Illegal sector count\n", PCX);
1091                                 wd179x_info->fdc_fmt_sector_count = 0;
1092                             }
1093                             wd179x_info->fdc_sectormap[wd179x_info->fdc_fmt_sector_count] = wd179x_info->fdc_sector;
1094                             wd179x_info->fdc_fmt_sector_count++;
1095                             sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
1096                                       " FMT Data Length = %d\n", PCX, wd179x_info->fdc_dataindex);
1097 
1098                             sim_debug(FMT_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT
1099                                       " FORMAT T:%d/H:%d/N:%d=%d/L=%d[%d] Fill=0x%02x\n", PCX,
1100                                       pDrive->track, wd179x_info->fdc_head,
1101                                       wd179x_info->fdc_fmt_sector_count,
1102                                       wd179x_info->fdc_sectormap[wd179x_info->fdc_fmt_sector_count],
1103                                       wd179x_info->fdc_dataindex, wd179x_info->fdc_sec_len, sdata.raw[0]);
1104 
1105                             wd179x_info->fdc_gap[1] = 0;
1106                             wd179x_info->fdc_fmt_state = FMT_GAP2;
1107 
1108                             if(wd179x_info->fdc_fmt_sector_count == max_sectors_per_track[wd179x_info->ddens & 1][wd179x_info->fdc_sec_len]) {
1109                                 trackWrite(pDrive->imd,
1110                                            pDrive->track,
1111                                            wd179x_info->fdc_head,
1112                                            wd179x_info->fdc_fmt_sector_count,
1113                                            wd179x_info->fdc_sec_len,
1114                                            wd179x_info->fdc_sectormap,
1115                                            wd179x_info->ddens ? 3 : 0, /* data mode */
1116                                            sdata.raw[0],
1117                                            &flags);
1118 
1119                                 wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY | WD179X_STAT_LOST_DATA);     /* Clear BUSY, LOST_DATA */
1120                                 wd179x_info->drq = 0;
1121                                 wd179x_info->intrq = 1;
1122 
1123                                 /* Recalculate disk size */
1124                                 pDrive->uptr->capac = sim_fsize(pDrive->uptr->fileref);
1125                             }
1126                         }
1127                     }
1128             }
1129 
1130             wd179x_info->fdc_data = cData;
1131             break;
1132     }
1133 
1134     return 0;
1135 }
1136 
1137