/************************************************************************* * * * Copyright (c) 2007-2020 Howard M. Harte. * * https://github.com/hharte * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to * * the following conditions: * * * * The above copyright notice and this permission notice shall be * * included in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * * * Except as contained in this notice, the name of Howard M. Harte shall * * not be used in advertising or otherwise to promote the sale, use or * * other dealings in this Software without prior written authorization * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Generic WD179X Disk Controller module for SIMH. * * * *************************************************************************/ #include "altairz80_defs.h" #include "sim_imd.h" #include "wd179x.h" #define CROMFDC_SIM_100US 291 /* Number of "ticks" in 100uS, where does this come from? */ #define CROMFDC_8IN_ROT (167 * CROMFDC_SIM_100US) #define CROMFDC_5IN_ROT (200 * CROMFDC_SIM_100US) /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define FMT_MSG (1 << 6) #define VERBOSE_MSG (1 << 7) #define IMD_MSG (1 << 8) #define WD179X_MAX_DRIVES 4 #define WD179X_SECTOR_LEN 8192 /* 2^(7 + WD179X_MAX_SEC_LEN) == WD179X_SECTOR_LEN */ #define WD179X_MAX_SEC_LEN 6 #define WD179X_MAX_SECTOR 26 #define CMD_PHASE 0 #define EXEC_PHASE 1 #define DATA_PHASE 2 /* Status Bits for Type I Commands */ #define WD179X_STAT_NOT_READY (1 << 7) #define WD179X_STAT_WPROT (1 << 6) #define WD179X_STAT_HLD (1 << 5) #define WD179X_STAT_SEEK_ERROR (1 << 4) #define WD179X_STAT_CRC_ERROR (1 << 3) #define WD179X_STAT_TRACK0 (1 << 2) #define WD179X_STAT_INDEX (1 << 1) #define WD179X_STAT_BUSY (1 << 0) /* Status Bits for Type II, III Commands */ /*#define WD179X_STAT_NOT_READY (1 << 7) */ /*#define WD179X_STAT_WPROT (1 << 6) */ #define WD179X_STAT_REC_TYPE (1 << 5) /* Also Write Fault */ #define WD179X_STAT_NOT_FOUND (1 << 4) /*#define WD179X_STAT_CRC_ERROR (1 << 3) */ #define WD179X_STAT_LOST_DATA (1 << 2) #define WD179X_STAT_DRQ (1 << 1) /*#define WD179X_STAT_BUSY (1 << 0) */ typedef union { uint8 raw[WD179X_SECTOR_LEN]; } SECTOR_FORMAT; typedef struct { UNIT *uptr; DISK_INFO *imd; uint8 ntracks; /* number of tracks */ uint8 nheads; /* number of heads */ uint32 sectsize; /* sector size, not including pre/postamble */ uint8 track; /* Current Track */ uint8 ready; /* Is drive ready? */ } WD179X_DRIVE_INFO; typedef struct { PNP_INFO pnp; /* Plug-n-Play Information */ uint8 intrq; /* WD179X Interrupt Request Output (EOJ) */ uint8 hld; /* WD179X Head Load Output */ uint8 drq; /* WD179X DMA Request Output */ uint8 ddens; /* WD179X Double-Density Input */ uint8 fdc_head; /* H Head Number */ uint8 sel_drive; /* Currently selected drive */ uint8 drivetype; /* 8 or 5 depending on disk type. */ uint8 fdc_status; /* WD179X Status Register */ uint8 verify; /* WD179X Type 1 command Verify flag */ uint8 fdc_data; /* WD179X Data Register */ uint8 fdc_read; /* TRUE when reading */ uint8 fdc_write; /* TRUE when writing */ uint8 fdc_write_track; /* TRUE when writing an entire track */ uint8 fdc_fmt_state; /* Format track statemachine state */ uint8 fdc_gap[4]; /* Gap I - Gap IV lengths */ uint8 fdc_fmt_sector_count; /* sector count for format track */ uint8 fdc_sectormap[WD179X_MAX_SECTOR]; /* Physical to logical sector map */ uint8 fdc_header_index; /* Index into header */ uint8 fdc_read_addr; /* TRUE when READ ADDRESS command is in progress */ uint8 fdc_multiple; /* TRUE for multi-sector read/write */ uint16 fdc_datacount; /* Read or Write data remaining transfer length */ uint16 fdc_dataindex; /* index of current byte in sector data */ uint8 index_pulse_wait; /* TRUE if waiting for interrupt on next index pulse. */ uint8 fdc_sector; /* R Record (Sector) */ uint8 fdc_sec_len; /* N Sector Length */ int8 step_dir; uint8 cmdtype; /* Type of current/former command */ WD179X_DRIVE_INFO drive[WD179X_MAX_DRIVES]; } WD179X_INFO; static SECTOR_FORMAT sdata; extern uint32 PCX; extern t_stat set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), const char* name, uint8 unmap); extern int32 find_unit_index (UNIT *uptr); t_stat wd179x_svc (UNIT *uptr); /* These are needed for DMA. PIO Mode has not been implemented yet. */ extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); extern uint8 GetBYTEWrapper(const uint32 Addr); #define UNIT_V_WD179X_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_WD179X_WLK (1 << UNIT_V_WD179X_WLK) #define UNIT_V_WD179X_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_WD179X_VERBOSE (1 << UNIT_V_WD179X_VERBOSE) #define WD179X_CAPACITY_SSSD (77*1*26*128) /* Single-sided Single Density IBM Diskette1 */ /* Write Track (format) Statemachine states */ #define FMT_GAP1 1 #define FMT_GAP2 2 #define FMT_GAP3 3 #define FMT_GAP4 4 #define FMT_HEADER 5 #define FMT_DATA 6 /* WD179X Commands */ #define WD179X_RESTORE 0x00 /* Type I */ #define WD179X_SEEK 0x10 /* Type I */ #define WD179X_STEP 0x20 /* Type I */ #define WD179X_STEP_U 0x30 /* Type I */ #define WD179X_STEP_IN 0x40 /* Type I */ #define WD179X_STEP_IN_U 0x50 /* Type I */ #define WD179X_STEP_OUT 0x60 /* Type I */ #define WD179X_STEP_OUT_U 0x70 /* Type I */ #define WD179X_READ_REC 0x80 /* Type II */ #define WD179X_READ_RECS 0x90 /* Type II */ #define WD179X_WRITE_REC 0xA0 /* Type II */ #define WD179X_WRITE_RECS 0xB0 /* Type II */ #define WD179X_READ_ADDR 0xC0 /* Type III */ #define WD179X_FORCE_INTR 0xD0 /* Type IV */ #define WD179X_READ_TRACK 0xE0 /* Type III */ #define WD179X_WRITE_TRACK 0xF0 /* Type III */ static int32 wd179xdev(const int32 port, const int32 io, const int32 data); static t_stat wd179x_reset(DEVICE *dptr); static const char* wd179x_description(DEVICE *dptr); uint8 floorlog2(unsigned int n); static uint8 computeSectorSize(const WD179X_DRIVE_INFO *pDrive); static uint8 testMode(const WD179X_DRIVE_INFO *pDrive); WD179X_INFO wd179x_info_data = { { 0x0, 0, 0x30, 4 } }; WD179X_INFO *wd179x_info = &wd179x_info_data; WD179X_INFO_PUB *wd179x_infop = (WD179X_INFO_PUB *)&wd179x_info_data; static UNIT wd179x_unit[] = { { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY_SSSD), 58200 }, { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY_SSSD), 58200 }, { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY_SSSD), 58200 }, { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY_SSSD), 58200 } }; static REG wd179x_reg[] = { { FLDATAD(INTRQ, wd179x_info_data.intrq, 1, "Interrupt Request"), }, { FLDATAD(HLD, wd179x_info_data.hld, 1, "Head Load"), }, { FLDATAD(DRQ, wd179x_info_data.drq, 1, "DMA Request"), }, { FLDATAD(DDENS, wd179x_info_data.ddens, 1, "Double Density"), }, { FLDATAD(HEAD, wd179x_info_data.fdc_head, 1, "Selected head"), }, { FLDATAD(DRIVE, wd179x_info_data.sel_drive, 2, "Selected drive"), }, { FLDATAD(DRIVETYPE, wd179x_info_data.drivetype, 1, "Drive Type"), }, { HRDATAD(STATUS, wd179x_info_data.fdc_status, 8, "Status Register"), }, { FLDATAD(VERIFY, wd179x_info_data.drivetype, 1, "Type 1 cmd Verify flag"), }, { HRDATAD(DATA, wd179x_info_data.fdc_data, 8, "Data Register"), }, { FLDATAD(READ, wd179x_info_data.fdc_read, 1, "True when reading"), }, { FLDATAD(WRITE, wd179x_info_data.fdc_write, 1, "True when writing"), }, { FLDATAD(WRITETRK, wd179x_info_data.fdc_write_track, 1, "True when writing"), }, { HRDATAD(FMTSTATE, wd179x_info_data.fdc_fmt_state, 8, "Format state machine"), }, { HRDATAD(GAP1, wd179x_info_data.fdc_gap[0], 8, "Gap I length"), }, { HRDATAD(GAP2, wd179x_info_data.fdc_gap[1], 8, "Gap II length"), }, { HRDATAD(GAP3, wd179x_info_data.fdc_gap[2], 8, "Gap III length"), }, { HRDATAD(GAP4, wd179x_info_data.fdc_gap[3], 8, "Gap IV length"), }, { HRDATAD(FMTSECCNT, wd179x_info_data.fdc_fmt_sector_count, 8, "Format sector count"), }, { HRDATAD(DATACOUNT, wd179x_info_data.fdc_datacount, 8, "Read or Write Remaining transfer length"), }, { HRDATAD(DATAINDEX, wd179x_info_data.fdc_dataindex, 8, "Index of current byte in sector"), }, { HRDATAD(SECTOR, wd179x_info_data.fdc_sector, 8, "Current sector"), }, { HRDATAD(SECLEN, wd179x_info_data.fdc_sec_len, 8, "Sector Length"), }, { FLDATAD(STEPDIR, wd179x_info_data.step_dir, 1, "Step direction"), }, { FLDATAD(IDXWAIT, wd179x_info_data.index_pulse_wait, 1, "Waiting for interrupt on next index"), }, { HRDATAD(CMDTYPE, wd179x_info_data.cmdtype, 8, "Current FDC command"), }, { NULL } }; #define WD179X_NAME "Western Digital FDC Core" static const char* wd179x_description(DEVICE *dptr) { return WD179X_NAME; } static MTAB wd179x_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL, "Sets disk controller I/O base address" }, { UNIT_WD179X_WLK, 0, "WRTENB", "WRTENB", NULL, NULL, NULL, "Enables " WD179X_NAME "n for writing" }, { UNIT_WD179X_WLK, UNIT_WD179X_WLK, "WRTLCK", "WRTLCK", NULL, NULL, NULL, "Locks " WD179X_NAME "n for writing" }, /* quiet, no warning messages */ { UNIT_WD179X_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, NULL, "No verbose messages for unit " WD179X_NAME "n" }, /* verbose, show warning messages */ { UNIT_WD179X_VERBOSE, UNIT_WD179X_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, NULL, "Verbose messages for unit " WD179X_NAME "n" }, { 0 } }; /* Debug Flags */ static DEBTAB wd179x_dt[] = { { "ERROR", ERROR_MSG, "Error messages" }, { "SEEK", SEEK_MSG, "Seek messages" }, { "CMD", CMD_MSG, "Command messages" }, { "READ", RD_DATA_MSG, "Read messages" }, { "WRITE", WR_DATA_MSG, "Write messages" }, { "STATUS", STATUS_MSG, "Status messages" }, { "FMT", FMT_MSG, "Format messages" }, { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, { "IMD", IMD_MSG, "ImageDisk messages"}, { NULL, 0 } }; DEVICE wd179x_dev = { "WD179X", wd179x_unit, wd179x_reg, wd179x_mod, WD179X_MAX_DRIVES, 10, 31, 1, WD179X_MAX_DRIVES, WD179X_MAX_DRIVES, NULL, NULL, &wd179x_reset, NULL, &wd179x_attach, &wd179x_detach, &wd179x_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, wd179x_dt, NULL, NULL, NULL, NULL, NULL, &wd179x_description }; /* Unit service routine */ /* Used to generate INDEX pulses in response to a FORCE_INTR command */ t_stat wd179x_svc (UNIT *uptr) { if(wd179x_info->index_pulse_wait == TRUE) { wd179x_info->index_pulse_wait = FALSE; wd179x_info->intrq = 1; } return SCPE_OK; } /* Reset routine */ static t_stat wd179x_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, "wd179xdev", TRUE); } else { /* Connect I/O Ports at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, "wd179xdev", FALSE) != 0) { sim_printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } return SCPE_OK; } void wd179x_external_restore(void) { WD179X_DRIVE_INFO *pDrive; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { sim_debug(ERROR_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " Illegal drive selected, cannot restore.\n", PCX); return; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; if(pDrive->uptr == NULL) { sim_debug(ERROR_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " No drive selected, cannot restore.\n", PCX); return; } sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " External Restore drive to track 0\n", wd179x_info->sel_drive, PCX); pDrive->track = 0; } uint8 wd179x_get_nheads(void) { WD179X_DRIVE_INFO* pDrive; if (wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { sim_debug(ERROR_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " Illegal drive selected, cannot determine number of heads.\n", PCX); return 0; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; if (pDrive->uptr == NULL) { sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " Not attached, cannot determine number of heads.\n", PCX); return 0; } return(pDrive->nheads); } /* Attach routine */ t_stat wd179x_attach(UNIT *uptr, CONST char *cptr) { char header[4]; t_stat r; int32 i = 0; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ uptr->capac = sim_fsize(uptr->fileref); i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } sim_debug(VERBOSE_MSG, &wd179x_dev, "Attach WD179X%d\n", i); wd179x_info->drive[i].uptr = uptr; /* Default to drive not ready */ wd179x_info->drive[i].ready = 0; uptr->u3 = IMAGE_TYPE_IMD; if(uptr->capac > 0) { char *rtn = fgets(header, 4, uptr->fileref); if ((rtn != NULL) && strncmp(header, "IMD", 3)) { sim_printf("WD179X: Only IMD disk images are supported\n"); wd179x_info->drive[i].uptr = NULL; return SCPE_OPENERR; } } else { /* create a disk image file in IMD format. */ if (diskCreate(uptr->fileref, "$Id: wd179x.c 1999 2008-07-22 04:25:28Z hharte $") != SCPE_OK) { sim_printf("WD179X: Failed to create IMD disk.\n"); wd179x_info->drive[i].uptr = NULL; return SCPE_OPENERR; } uptr->capac = sim_fsize(uptr->fileref); } sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X[%d]: attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : "DSK", uptr->capac); if(uptr->u3 == IMAGE_TYPE_IMD) { sim_debug(VERBOSE_MSG, &wd179x_dev, "--------------------------------------------------------\n"); wd179x_info->drive[i].imd = diskOpenEx(uptr->fileref, uptr->flags & UNIT_WD179X_VERBOSE, &wd179x_dev, IMD_MSG, IMD_MSG); sim_debug(VERBOSE_MSG, &wd179x_dev, "\n"); if (wd179x_info->drive[i].imd == NULL) { sim_printf("WD179X: IMD disk corrupt.\n"); wd179x_info->drive[i].uptr = NULL; return SCPE_OPENERR; } /* Write-protect the unit if IMD think's it's writelocked. */ if(imdIsWriteLocked(wd179x_info->drive[i].imd)) { uptr->flags |= UNIT_WD179X_WLK; } /* Set the correct number of sides for this disk image. */ wd179x_info->drive[i].nheads = imdGetSides(wd179x_info->drive[i].imd); } else { wd179x_info->drive[i].imd = NULL; wd179x_info->fdc_sec_len = 0; /* 128 byte sectors */ } wd179x_info->drive[i].ready = 1; wd179x_info->sel_drive = 0; return SCPE_OK; } /* Detach routine */ t_stat wd179x_detach(UNIT *uptr) { t_stat r; int8 i; i = find_unit_index(uptr); if (i == -1) { return SCPE_IERR; } sim_debug(VERBOSE_MSG, &wd179x_dev, "Detach WD179X%d\n", i); r = diskClose(&wd179x_info->drive[i].imd); wd179x_info->drive[i].ready = 0; if (r != SCPE_OK) return r; r = detach_unit(uptr); /* detach unit */ if (r != SCPE_OK) return r; return SCPE_OK; } static int32 wd179xdev(const int32 port, const int32 io, const int32 data) { int32 result = 0; if(io) { sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x\n", PCX, io ? "OUT" : " IN", port, data); WD179X_Write(port, data); } else { result = WD179X_Read(port); sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x\n", PCX, io ? "OUT" : " IN", port, result); } return (result); } uint8 floorlog2(unsigned int n) { /* Compute log2(n) */ uint8 r = 0; if (n >= 1<<16) { n >>=16; r += 16; } if (n >= 1<< 8) { n >>= 8; r += 8; } if (n >= 1<< 4) { n >>= 4; r += 4; } if (n >= 1<< 2) { n >>= 2; r += 2; } if (n >= 1<< 1) { r += 1; } return ((n == 0) ? (0xFF) : r); /* 0xFF is error return value */ } static uint8 computeSectorSize(const WD179X_DRIVE_INFO *pDrive) { return pDrive->track < MAX_CYL ? floorlog2(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7 : 0xF8; } static uint8 testMode(const WD179X_DRIVE_INFO *pDrive) { return pDrive->track < MAX_CYL ? IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens) : 0; } uint8 WD179X_Read(const uint32 Addr) { uint8 cData; WD179X_DRIVE_INFO *pDrive; uint32 flags = 0; uint32 readlen; int status = SCPE_OK; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { return 0xFF; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; cData = 0x00; switch(Addr & 0x3) { case WD179X_STATUS: /* Fix up status based on Command Type */ if((wd179x_info->cmdtype == 0) || (wd179x_info->cmdtype == 1) || (wd179x_info->cmdtype == 4)) { wd179x_info->fdc_status ^= WD179X_STAT_INDEX; /* Generate Index pulses */ wd179x_info->fdc_status &= ~WD179X_STAT_TRACK0; wd179x_info->fdc_status |= (pDrive->track == 0) ? WD179X_STAT_TRACK0 : 0; } else if(wd179x_info->cmdtype == 4) { } else { wd179x_info->fdc_status &= ~WD179X_STAT_INDEX; /* Mask index pulses */ wd179x_info->fdc_status |= (wd179x_info->drq) ? WD179X_STAT_DRQ : 0; } cData = (pDrive->ready == 0) ? WD179X_STAT_NOT_READY : 0; cData |= wd179x_info->fdc_status; /* Status Register */ sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " RD STATUS = 0x%02x, CMDTYPE=%x\n", PCX, cData, wd179x_info->cmdtype); wd179x_info->intrq = 0; break; case WD179X_TRACK: cData = pDrive->track; sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " RD TRACK = 0x%02x\n", PCX, cData); break; case WD179X_SECTOR: cData = wd179x_info->fdc_sector; sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " RD SECT = 0x%02x\n", PCX, cData); break; case WD179X_DATA: cData = 0xFF; /* Return High-Z data */ if(wd179x_info->fdc_read == TRUE) { if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) { cData = sdata.raw[wd179x_info->fdc_dataindex]; if(wd179x_info->fdc_read_addr == TRUE) { sim_debug(STATUS_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " READ_ADDR[%d] = 0x%02x\n", wd179x_info->sel_drive, PCX, wd179x_info->fdc_dataindex, cData); } wd179x_info->fdc_dataindex++; if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) { if(wd179x_info->fdc_multiple == FALSE) { wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ wd179x_info->drq = 0; wd179x_info->intrq = 1; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; } else { /* Compute Sector Size */ wd179x_info->fdc_sec_len = computeSectorSize(pDrive); if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!\n", wd179x_info->sel_drive, PCX); wd179x_info->fdc_sec_len = 0; return cData; } wd179x_info->fdc_sector ++; sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " MULTI_READ_REC, T:%2d/S:%d/N:%2d, %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); status = sectRead(pDrive->imd, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, sdata.raw, 128 << wd179x_info->fdc_sec_len, &flags, &readlen); if(status == SCPE_OK) { wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */ wd179x_info->drq = 1; wd179x_info->intrq = 0; wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_read = TRUE; wd179x_info->fdc_read_addr = FALSE; } else { wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */ wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; wd179x_info->drq = 0; wd179x_info->intrq = 1; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; } } } } } break; } return (cData); } /* * Command processing happens in three stages: * 1. Flags and initial conditions are set up based on the Type of the command. * 2. The execution phase takes place. * 3. Status is updated based on the Type and outcome of the command execution. * * See the WD179x-02 Datasheet available on www.hartetechnologies.com/manuals/ * */ static uint8 Do1793Command(uint8 cCommand) { uint8 result = 0; WD179X_DRIVE_INFO *pDrive; uint32 flags = 0; uint32 readlen; int status; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { return 0xFF; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } if(wd179x_info->fdc_status & WD179X_STAT_BUSY) { if(((cCommand & 0xF0) != WD179X_FORCE_INTR)) { /* && ((cCommand & 0xF0) != WD179X_RESTORE)) { */ sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " ERROR: Command 0x%02x ignored because controller is BUSY\n\n", wd179x_info->sel_drive, PCX, cCommand); } return 0xFF; } wd179x_info->fdc_status &= ~WD179X_STAT_NOT_READY; /* Extract Type-specific command flags, and set initial conditions */ switch(cCommand & 0xF0) { /* Type I Commands */ case WD179X_RESTORE: case WD179X_SEEK: case WD179X_STEP: case WD179X_STEP_U: case WD179X_STEP_IN: case WD179X_STEP_IN_U: case WD179X_STEP_OUT: case WD179X_STEP_OUT_U: wd179x_info->cmdtype = 1; wd179x_info->fdc_status |= WD179X_STAT_BUSY; /* Set BUSY */ wd179x_info->fdc_status &= ~(WD179X_STAT_CRC_ERROR | WD179X_STAT_SEEK_ERROR | WD179X_STAT_DRQ); wd179x_info->intrq = 0; wd179x_info->hld = cCommand & 0x08; wd179x_info->verify = cCommand & 0x04; break; /* Type II Commands */ case WD179X_READ_REC: case WD179X_READ_RECS: case WD179X_WRITE_REC: case WD179X_WRITE_RECS: wd179x_info->cmdtype = 2; wd179x_info->fdc_status = WD179X_STAT_BUSY; /* Set BUSY, clear all others */ wd179x_info->intrq = 0; wd179x_info->hld = 1; /* Load the head immediately, E Flag not checked. */ break; /* Type III Commands */ case WD179X_READ_ADDR: case WD179X_READ_TRACK: case WD179X_WRITE_TRACK: wd179x_info->cmdtype = 3; break; /* Type IV Commands */ case WD179X_FORCE_INTR: wd179x_info->cmdtype = 4; break; default: wd179x_info->cmdtype = 0; break; } switch(cCommand & 0xF0) { /* Type I Commands */ case WD179X_RESTORE: sim_debug(CMD_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=RESTORE %s\n", wd179x_info->sel_drive, PCX, wd179x_info->verify ? "[VERIFY]" : ""); pDrive->track = 0; wd179x_info->intrq = 1; break; case WD179X_SEEK: sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=SEEK, track=%d, new=%d\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_data); pDrive->track = wd179x_info->fdc_data; break; case WD179X_STEP: sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP\n", wd179x_info->sel_drive, PCX); break; case WD179X_STEP_U: sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_U dir=%d\n", wd179x_info->sel_drive, PCX, wd179x_info->step_dir); if(wd179x_info->step_dir == 1) { if (pDrive->track < MAX_CYL - 1) pDrive->track++; } else if (wd179x_info->step_dir == -1) { if (pDrive->track > 0) pDrive->track--; } else { sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " ERROR: undefined direction for STEP\n", wd179x_info->sel_drive, PCX); } break; case WD179X_STEP_IN: sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_IN\n", wd179x_info->sel_drive, PCX); break; case WD179X_STEP_IN_U: if (pDrive->track < MAX_CYL - 1) pDrive->track++; wd179x_info->step_dir = 1; sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_IN_U, Track=%d\n", wd179x_info->sel_drive, PCX, pDrive->track); break; case WD179X_STEP_OUT: sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_OUT\n", wd179x_info->sel_drive, PCX); break; case WD179X_STEP_OUT_U: sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_OUT_U\n", wd179x_info->sel_drive, PCX); if (pDrive->track > 0) pDrive->track--; wd179x_info->step_dir = -1; break; /* Type II Commands */ case WD179X_READ_REC: case WD179X_READ_RECS: /* Compute Sector Size */ wd179x_info->fdc_sec_len = computeSectorSize(pDrive); if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!\n", wd179x_info->sel_drive, PCX); wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; /* Sector not found */ wd179x_info->fdc_status &= ~WD179X_STAT_BUSY; wd179x_info->intrq = 1; wd179x_info->drq = 0; wd179x_info->fdc_sec_len = 0; return 0xFF; } wd179x_info->fdc_multiple = (cCommand & 0x10) ? TRUE : FALSE; sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_REC, T:%2d/S:%d/N:%2d, %s, %s len=%d\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, wd179x_info->fdc_multiple ? "Multiple" : "Single", wd179x_info->ddens ? "DD" : "SD", 128 << wd179x_info->fdc_sec_len); if(testMode(pDrive)) { wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; /* Sector not found */ wd179x_info->fdc_status &= ~WD179X_STAT_BUSY; wd179x_info->intrq = 1; wd179x_info->drq = 0; } else { status = sectRead(pDrive->imd, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, sdata.raw, 128 << wd179x_info->fdc_sec_len, &flags, &readlen); if(status == SCPE_OK) { wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */ wd179x_info->drq = 1; wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_write = FALSE; wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_read = TRUE; wd179x_info->fdc_read_addr = FALSE; } else { wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */ wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; wd179x_info->fdc_status &= ~WD179X_STAT_BUSY; wd179x_info->drq = 0; wd179x_info->intrq = 1; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; } } break; case WD179X_WRITE_RECS: sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Error: WRITE_RECS not implemented.\n", wd179x_info->sel_drive, PCX); break; case WD179X_WRITE_REC: /* Compute Sector Size */ wd179x_info->fdc_sec_len = computeSectorSize(pDrive); if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!\n", wd179x_info->sel_drive, PCX); wd179x_info->fdc_sec_len = 0; } sim_debug(WR_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_REC, T:%2d/S:%d/N:%2d, %s.\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, (cCommand & 0x10) ? "Multiple" : "Single"); wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */ wd179x_info->drq = 1; wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_write = TRUE; wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; sdata.raw[wd179x_info->fdc_dataindex] = wd179x_info->fdc_data; break; /* Type III Commands */ case WD179X_READ_ADDR: sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_ADDR, T:%d/S:%d, %s\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->ddens ? "DD" : "SD"); /* For some reason 86-DOS tries to use this track, force it to 0. Need to investigate this more. */ if (pDrive->track == 0xFF) pDrive->track=0; /* Compute Sector Size */ wd179x_info->fdc_sec_len = computeSectorSize(pDrive); if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!\n", wd179x_info->sel_drive, PCX); wd179x_info->fdc_sec_len = 0; } if(testMode(pDrive)) { wd179x_info->fdc_status = WD179X_STAT_NOT_FOUND; /* Sector not found */ wd179x_info->intrq = 1; } else { wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */ wd179x_info->drq = 1; wd179x_info->fdc_datacount = 6; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_read = TRUE; wd179x_info->fdc_read_addr = TRUE; sdata.raw[0] = pDrive->track; sdata.raw[1] = wd179x_info->fdc_head; sdata.raw[2] = wd179x_info->fdc_sector; sdata.raw[3] = wd179x_info->fdc_sec_len; sdata.raw[4] = 0xAA; /* CRC1 */ sdata.raw[5] = 0x55; /* CRC2 */ wd179x_info->fdc_sector = pDrive->track; wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ wd179x_info->intrq = 1; } break; case WD179X_READ_TRACK: sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_TRACK\n", wd179x_info->sel_drive, PCX); sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Error: READ_TRACK not implemented.\n", wd179x_info->sel_drive, PCX); break; case WD179X_WRITE_TRACK: sim_debug(WR_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_TRACK\n", wd179x_info->sel_drive, PCX); sim_debug(FMT_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_TRACK, T:%2d/S:%d.\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head); wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */ wd179x_info->drq = 1; wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_write = FALSE; wd179x_info->fdc_write_track = TRUE; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; wd179x_info->fdc_fmt_state = FMT_GAP1; /* TRUE when writing an entire track */ wd179x_info->fdc_fmt_sector_count = 0; break; /* Type IV Commands */ case WD179X_FORCE_INTR: sim_debug(CMD_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " CMD=FORCE_INTR\n", wd179x_info->sel_drive, PCX); if((cCommand & 0x0F) == 0) { /* I0-I3 == 0, no intr, but clear BUSY and terminate command */ wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ wd179x_info->drq = 0; wd179x_info->fdc_write = FALSE; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_read_addr = FALSE; wd179x_info->fdc_datacount = 0; wd179x_info->fdc_dataindex = 0; } else { if(wd179x_info->fdc_status & WD179X_STAT_BUSY) { /* Force Interrupt when command is pending */ } else { /* Command not pending, clear status */ wd179x_info->fdc_status = 0; } if(cCommand & 0x04) { wd179x_info->index_pulse_wait = TRUE; if(wd179x_info->sel_drive < WD179X_MAX_DRIVES) { sim_activate (wd179x_unit, ((wd179x_info->drive[wd179x_info->sel_drive].imd->ntracks % 77) == 0) ? CROMFDC_8IN_ROT : CROMFDC_5IN_ROT); /* Generate INDEX pulse */ /* sim_printf("Drive %d Num tracks=%d\n", wd179x_info->sel_drive, wd179x_info->drive[wd179x_info->sel_drive].imd->ntracks); */ } } else { wd179x_info->intrq = 1; } wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ } break; default: sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " ERROR: Unknown command 0x%02x.\n\n", wd179x_info->sel_drive, PCX, cCommand); break; } /* Post processing of Type-specific command */ switch(cCommand & 0xF0) { /* Type I Commands */ case WD179X_RESTORE: case WD179X_SEEK: case WD179X_STEP: case WD179X_STEP_U: case WD179X_STEP_IN: case WD179X_STEP_IN_U: case WD179X_STEP_OUT: case WD179X_STEP_OUT_U: if(wd179x_info->verify) { /* Verify the selected track/head is ok. */ sim_debug(SEEK_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Verify ", wd179x_info->sel_drive, PCX); if(sectSeek(pDrive->imd, pDrive->track, wd179x_info->fdc_head) != SCPE_OK) { sim_debug(SEEK_MSG, &wd179x_dev, "FAILED\n"); wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; } else if(testMode(pDrive)) { wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; /* Sector not found */ sim_debug(SEEK_MSG, &wd179x_dev, "NOT FOUND\n"); } else { sim_debug(SEEK_MSG, &wd179x_dev, "Ok\n"); } } if(pDrive->track == 0) { wd179x_info->fdc_status |= WD179X_STAT_TRACK0; } else { wd179x_info->fdc_status &= ~(WD179X_STAT_TRACK0); } wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ wd179x_info->intrq = 1; wd179x_info->drq = 1; break; /* Type II Commands */ case WD179X_READ_REC: case WD179X_READ_RECS: case WD179X_WRITE_REC: case WD179X_WRITE_RECS: /* Type III Commands */ case WD179X_READ_ADDR: case WD179X_READ_TRACK: case WD179X_WRITE_TRACK: /* Type IV Commands */ case WD179X_FORCE_INTR: default: break; } return result; } /* Maximum number of sectors per track for format */ uint8 max_sectors_per_track[2][7] = { /* 128, 256, 512, 1024, 2048, 4096, 8192 */ { 26, 15, 8, 4, 2, 1, 0 }, /* Single-density table */ { 26, 26, 15, 8, 4, 2, 1 } /* Double-density table */ }; uint8 WD179X_Write(const uint32 Addr, uint8 cData) { WD179X_DRIVE_INFO *pDrive; /* uint8 disk_read = 0; */ uint32 flags = 0; uint32 writelen; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { return 0xFF; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } switch(Addr & 0x3) { case WD179X_STATUS: sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " WR CMD = 0x%02x\n", PCX, cData); wd179x_info->fdc_read = FALSE; wd179x_info->fdc_write = FALSE; wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_datacount = 0; wd179x_info->fdc_dataindex = 0; Do1793Command(cData); break; case WD179X_TRACK: sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " WR TRACK = 0x%02x\n", PCX, cData); pDrive->track = cData; break; case WD179X_SECTOR: /* Sector Register */ sim_debug(STATUS_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " WR SECT = 0x%02x\n", PCX, cData); wd179x_info->fdc_sector = cData; break; case WD179X_DATA: sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " WR DATA = 0x%02x\n", PCX, cData); if(wd179x_info->fdc_write == TRUE) { if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) { sdata.raw[wd179x_info->fdc_dataindex] = cData; wd179x_info->fdc_dataindex++; if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) { wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ wd179x_info->drq = 0; wd179x_info->intrq = 1; sim_debug(WR_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Writing sector, T:%2d/S:%d/N:%2d, Len=%d\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, 128 << wd179x_info->fdc_sec_len); sectWrite(pDrive->imd, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, sdata.raw, 128 << wd179x_info->fdc_sec_len, &flags, &writelen); wd179x_info->fdc_write = FALSE; } } } if(wd179x_info->fdc_write_track == TRUE) { if(wd179x_info->fdc_fmt_state == FMT_GAP1) { if(cData != 0xFC) { wd179x_info->fdc_gap[0]++; } else { sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FMT GAP1 Length = %d\n", PCX, wd179x_info->fdc_gap[0]); wd179x_info->fdc_gap[1] = 0; wd179x_info->fdc_fmt_state = FMT_GAP2; } } else if(wd179x_info->fdc_fmt_state == FMT_GAP2) { if(cData != 0xFE) { wd179x_info->fdc_gap[1]++; } else { sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FMT GAP2 Length = %d\n", PCX, wd179x_info->fdc_gap[1]); wd179x_info->fdc_gap[2] = 0; wd179x_info->fdc_fmt_state = FMT_HEADER; wd179x_info->fdc_header_index = 0; } } else if(wd179x_info->fdc_fmt_state == FMT_HEADER) { if(wd179x_info->fdc_header_index == 5) { wd179x_info->fdc_gap[2] = 0; wd179x_info->fdc_fmt_state = FMT_GAP3; } else { sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " HEADER[%d]=%02x\n", PCX, wd179x_info->fdc_header_index, cData); switch(wd179x_info->fdc_header_index) { case 0: pDrive->track = cData; break; case 1: wd179x_info->fdc_head = cData; break; case 2: wd179x_info->fdc_sector = cData; break; case 3: if(cData != 0x00) { } break; case 4: if(cData != 0xF7) { } break; } wd179x_info->fdc_header_index++; } } else if(wd179x_info->fdc_fmt_state == FMT_GAP3) { if(cData != 0xFB) { wd179x_info->fdc_gap[2]++; } else { sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FMT GAP3 Length = %d\n", PCX, wd179x_info->fdc_gap[2]); wd179x_info->fdc_fmt_state = FMT_DATA; wd179x_info->fdc_dataindex = 0; } } else if(wd179x_info->fdc_fmt_state == FMT_DATA) { /* data bytes */ if(cData != 0xF7) { sdata.raw[wd179x_info->fdc_dataindex] = cData; wd179x_info->fdc_dataindex++; } else { wd179x_info->fdc_sec_len = floorlog2(wd179x_info->fdc_dataindex) - 7; if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!\n", wd179x_info->sel_drive, PCX); wd179x_info->fdc_sec_len = 0; } if(wd179x_info->fdc_fmt_sector_count >= WD179X_MAX_SECTOR) { sim_debug(ERROR_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " Illegal sector count\n", PCX); wd179x_info->fdc_fmt_sector_count = 0; } wd179x_info->fdc_sectormap[wd179x_info->fdc_fmt_sector_count] = wd179x_info->fdc_sector; wd179x_info->fdc_fmt_sector_count++; sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FMT Data Length = %d\n", PCX, wd179x_info->fdc_dataindex); sim_debug(FMT_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FORMAT T:%2d/H:%d/N:%2d=%d/L=%d[%d] Fill=0x%02x\n", PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_fmt_sector_count, wd179x_info->fdc_sectormap[wd179x_info->fdc_fmt_sector_count - 1], wd179x_info->fdc_dataindex, wd179x_info->fdc_sec_len, sdata.raw[0]); wd179x_info->fdc_gap[1] = 0; wd179x_info->fdc_fmt_state = FMT_GAP2; if(wd179x_info->fdc_fmt_sector_count == max_sectors_per_track[wd179x_info->ddens & 1][wd179x_info->fdc_sec_len]) { trackWrite(pDrive->imd, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_fmt_sector_count, 128 << wd179x_info->fdc_sec_len, wd179x_info->fdc_sectormap, wd179x_info->ddens ? 3 : 0, /* data mode */ sdata.raw[0], &flags); wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY | WD179X_STAT_LOST_DATA); /* Clear BUSY, LOST_DATA */ wd179x_info->drq = 0; wd179x_info->intrq = 1; /* Recalculate disk size */ pDrive->uptr->capac = sim_fsize(pDrive->uptr->fileref); } } } } wd179x_info->fdc_data = cData; break; } return 0; }