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