/* s100_tarbell.c: Tarbell 1011/2022 Disk Controller Created by Patrick Linstruth (patrick@deltecent.com) 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 PETER SCHORN 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 Patrick Linstruth shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Patrick Linstruth. These functions support simulated Tarbell model 1011 single density and model 2022 double density floppy disk controllers. The model is selected using the "SET TARBELL MODEL={SD|DD}" command. This device does not support the DMA feature of the double density controller. All software using this simulator must use programmed I/O. */ /* #define DBG_MSG */ #include "altairz80_defs.h" #include "sim_imd.h" #ifdef DBG_MSG #define DBG_PRINT(args) sim_printf args #else #define DBG_PRINT(args) #endif extern uint32 PCX; extern t_stat set_membase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); 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); /* These are needed for DMA. */ extern void PutByteDMA(const uint32 addr, const uint32 val); extern uint8 GetByteDMA(const uint32 addr); #define TARBELL_MAX_ADAPTERS 1 #define TARBELL_MAX_DRIVES 4 #define TARBELL_SECTOR_LEN 128 #define TARBELL_SPT_SD 26 #define TARBELL_SPT_DD 51 #define TARBELL_TRACKS 77 #define TARBELL_CAPACITY (256256) /* Default Tarbell Single Density Disk Capacity */ #define TARBELL_ROTATION_MS (166) /* 166 milliseconds per revolution */ #define TARBELL_HEAD_TIMEOUT (TARBELL_ROTATION_MS * 1000 * 2) /* usec * 2 revolutions */ #define IBM3740_TRK_HDR_LEN 73 #define IBM3740_SD_SEC_LEN 186 #define IBM3740_DD_SEC_LEN 196 #define TARBELL_PROM_SIZE 32 #define TARBELL_PROM_MASK (TARBELL_PROM_SIZE-1) #define TARBELL_RAM_SIZE 256 #define TARBELL_RAM_MASK (TARBELL_RAM_SIZE-1) #define TARBELL_PROM_READ FALSE #define TARBELL_PROM_WRITE TRUE #define TARBELL_MEMBASE 0x0000 #define TARBELL_MEMSIZE TARBELL_RAM_SIZE #define TARBELL_IOBASE 0xF8 #define TARBELL_IOSIZE_SD 5 #define TARBELL_IOSIZE_DD 6 #define TARBELL_DMABASE 0xE0 #define TARBELL_DMASIZE 16 /* Tarbell PROM is 32 bytes */ static uint8 tarbell_prom[TARBELL_PROM_SIZE] = { 0xdb, 0xfc, 0xaf, 0x6f, 0x67, 0x3c, 0xd3, 0xfa, 0x3e, 0x8c, 0xd3, 0xf8, 0xdb, 0xfc, 0xb7, 0xf2, 0x19, 0x00, 0xdb, 0xfb, 0x77, 0x23, 0xc3, 0x0c, 0x00, 0xdb, 0xf8, 0xb7, 0xca, 0x7d, 0x00, 0x76 }; static uint8 tarbell_ram[TARBELL_RAM_SIZE]; /* ** Western Digital FD17XX Registers and Interface Controls */ typedef struct { uint8 track; /* Track Register */ uint8 sector; /* Sector Register */ uint8 command; /* Command Register */ uint8 status; /* Status Register */ uint8 data; /* Data Register */ uint8 intrq; /* Interrupt Request */ int8 stepDir; /* Last Step Direction */ uint32 dataCount; /* Number of data bytes transferred from controller for current sector/address */ uint32 trkCount; /* Number of data bytes transferred from controller for current track */ uint8 readActive; /* Read Active */ uint8 readTrkActive; /* Read Track Active */ uint8 writeActive; /* Write Active */ uint8 writeTrkActive; /* Write Track Active */ uint8 dataAddrMrk; /* Data Addr Mark Flag */ uint8 addrActive; /* Address Active */ } FD17XX_REG; #define FD17XX_STAT_NOTREADY 0x80 #define FD17XX_STAT_WRITEPROT 0x40 #define FD17XX_STAT_RTYPEMSB 0x40 #define FD17XX_STAT_HEADLOAD 0x20 #define FD17XX_STAT_RTYPELSB 0x20 #define FD17XX_STAT_WRITEFAULT 0x20 #define FD17XX_STAT_SEEKERROR 0x10 #define FD17XX_STAT_NOTFOUND 0x10 #define FD17XX_STAT_CRCERROR 0x08 #define FD17XX_STAT_TRACK0 0x04 #define FD17XX_STAT_LOSTDATA 0x04 #define FD17XX_STAT_INDEX 0x02 #define FD17XX_STAT_DRQ 0x02 #define FD17XX_STAT_BUSY 0x01 typedef struct { PNP_INFO pnp; /* Plug and Play */ uint32 dma_base; /* DMA base I/O address */ uint32 dma_size; /* DMA I/O size */ uint32 ddEnabled; /* 0=SD,1=DD */ uint32 headTimeout; /* Head unload timer value */ uint8 promEnabled; /* PROM is enabled */ uint8 writeProtect; /* Write Protect is enabled */ uint8 currentDrive; /* currently selected drive */ uint8 secsPerTrack; /* sectors per track */ uint16 bytesPerTrack; /* bytes per track */ uint8 headLoaded[TARBELL_MAX_DRIVES]; /* Head Loaded */ uint8 doubleDensity[TARBELL_MAX_DRIVES]; /* true if double density */ uint8 side[TARBELL_MAX_DRIVES]; /* side 0 or side 1 */ FD17XX_REG FD17XX; /* FD17XX Registers and Data */ UNIT *uptr[TARBELL_MAX_DRIVES]; } TARBELL_INFO; static TARBELL_INFO tarbell_info_data = { { TARBELL_MEMBASE, TARBELL_MEMSIZE, TARBELL_IOBASE, TARBELL_IOSIZE_SD }, TARBELL_DMABASE, TARBELL_DMASIZE }; static TARBELL_INFO *tarbell_info = &tarbell_info_data; static uint8 sdata[TARBELL_SECTOR_LEN]; /* Tarbell Registers */ #define TARBELL_REG_STATUS 0x00 #define TARBELL_REG_COMMAND 0x00 #define TARBELL_REG_TRACK 0x01 #define TARBELL_REG_SECTOR 0x02 #define TARBELL_REG_DATA 0x03 #define TARBELL_REG_WAIT 0x04 #define TARBELL_REG_DRVSEL 0x04 #define TARBELL_REG_DMASTAT 0x05 #define TARBELL_REG_EXTADDR 0x05 /* Tarbell Commands */ #define TARBELL_CMD_RESTORE 0x00 #define TARBELL_CMD_SEEK 0x10 #define TARBELL_CMD_STEP 0x20 #define TARBELL_CMD_STEPU (TARBELL_CMD_STEP | TARBELL_FLAG_U) #define TARBELL_CMD_STEPIN 0x40 #define TARBELL_CMD_STEPINU (TARBELL_CMD_STEPIN | TARBELL_FLAG_U) #define TARBELL_CMD_STEPOUT 0x60 #define TARBELL_CMD_STEPOUTU (TARBELL_CMD_STEPOUT | TARBELL_FLAG_U) #define TARBELL_CMD_READ 0x80 #define TARBELL_CMD_READM (TARBELL_CMD_READM | TARBELL_FLAG_M) #define TARBELL_CMD_WRITE 0xA0 #define TARBELL_CMD_WRITEM (TARBELL_CMD_WRITEM | TARBELL_FLAG_M) #define TARBELL_CMD_READ_ADDRESS 0xC0 #define TARBELL_CMD_READ_TRACK 0xE0 #define TARBELL_CMD_WRITE_TRACK 0xF0 #define TARBELL_CMD_FORCE_INTR 0xD0 #define TARBELL_FLAG_V 0x04 #define TARBELL_FLAG_H 0x08 #define TARBELL_FLAG_U 0x10 #define TARBELL_FLAG_M 0x10 #define TARBELL_FLAG_B 0x08 #define TARBELL_FLAG_S 0x01 #define TARBELL_FLAG_E 0x04 #define TARBELL_FLAG_A1A0_FB 0x00 #define TARBELL_FLAG_A1A0_FA 0x01 #define TARBELL_FLAG_A1A0_F9 0x02 #define TARBELL_FLAG_A1A0_F8 0x03 #define TARBELL_FLAG_I0 0x01 #define TARBELL_FLAG_I1 0x02 #define TARBELL_FLAG_I2 0x04 #define TARBELL_FLAG_I3 0x08 #define TARBELL_FLAG_R1R0_6MS 0x00 #define TARBELL_FLAG_R1R0_10ms 0x02 #define TARBELL_FLAG_R1R0_20ms 0x03 #define TARBELL_ADDR_TRACK 0x00 #define TARBELL_ADDR_ZEROS 0x01 #define TARBELL_ADDR_SECTOR 0x02 #define TARBELL_ADDR_LENGTH 0x03 #define TARBELL_ADDR_CRC1 0x04 #define TARBELL_ADDR_CRC2 0x05 #define TARBELL_DENS_MASK 0x08 #define TARBELL_DSEL_MASK 0x30 #define TARBELL_SIDE_MASK 0x40 /* Local function prototypes */ static t_stat tarbell_reset(DEVICE *tarbell_dev); static t_stat tarbell_svc(UNIT *uptr); static t_stat tarbell_attach(UNIT *uptr, CONST char *cptr); static t_stat tarbell_detach(UNIT *uptr); static t_stat tarbell_boot(int32 unitno, DEVICE *dptr); static t_stat tarbell_set_dmabase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); static t_stat tarbell_show_dmabase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat tarbell_set_prom(UNIT *uptr, int32 val, CONST char *cptr, void *desc); static t_stat tarbell_show_prom(FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat tarbell_set_model(UNIT *uptr, int32 val, CONST char *cptr, void *desc); static t_stat tarbell_show_model(FILE *st, UNIT *uptr, int32 val, CONST void *desc); static uint32 secs_per_track(uint8 track); static uint32 bytes_per_track(uint8 track); static uint32 calculate_tarbell_sec_offset(uint8 track, uint8 sector); static void TARBELL_HeadLoad(UNIT *uptr, FD17XX_REG *pFD17XX, uint8 load); static uint8 TARBELL_Read(uint32 Addr); static uint8 TARBELL_Write(uint32 Addr, int32 data); static uint8 TARBELL_Command(UNIT *uptr, FD17XX_REG *pFD17XX, int32 data); static uint32 TARBELL_ReadSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer); static uint32 TARBELL_WriteSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer); static const char* tarbell_description(DEVICE *dptr); static void showdata(int32 isRead); static int32 tarbelldev(int32 Addr, int32 rw, int32 data); static int32 tarbelldma(int32 Addr, int32 rw, int32 data); static int32 tarbellprom(int32 Addr, int32 rw, int32 data); static UNIT tarbell_unit[TARBELL_MAX_DRIVES] = { { UDATA (tarbell_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, TARBELL_CAPACITY), 10000 }, { UDATA (tarbell_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, TARBELL_CAPACITY), 10000 }, { UDATA (tarbell_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, TARBELL_CAPACITY), 10000 }, { UDATA (tarbell_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, TARBELL_CAPACITY), 10000 } }; static REG tarbell_reg[] = { { DRDATAD (DRIVE, tarbell_info_data.currentDrive, 8, "Current drive register"), }, { HRDATAD (STATUS, tarbell_info_data.FD17XX.status, 8, "Status register"), }, { HRDATAD (COMMAND, tarbell_info_data.FD17XX.command, 8, "Command register"), }, { HRDATAD (DATA, tarbell_info_data.FD17XX.data, 8, "Data register"), }, { DRDATAD (TRACK, tarbell_info_data.FD17XX.track, 8, "Track register"), }, { DRDATAD (SECTOR, tarbell_info_data.FD17XX.sector, 8, "Sector register"), }, { DRDATAD (SPT, tarbell_info_data.secsPerTrack, 8, "Sectors per track register"), }, { DRDATAD (BPT, tarbell_info_data.bytesPerTrack, 16, "Bytes per track register"), }, { DRDATAD (STEPDIR, tarbell_info_data.FD17XX.stepDir, 8, "Last step direction register"), }, { DRDATAD (SECCNT, tarbell_info_data.FD17XX.dataCount, 16, "Sector byte count register"), }, { DRDATAD (TRKCNT, tarbell_info_data.FD17XX.trkCount, 16, "Track byte count register"), }, { FLDATAD (RDACT, tarbell_info_data.FD17XX.readActive, 0, "Read sector active status bit"), }, { FLDATAD (WRACT, tarbell_info_data.FD17XX.writeActive, 0, "Write sector active status bit"), }, { FLDATAD (RDTACT, tarbell_info_data.FD17XX.readTrkActive, 0, "Read track active status bit"), }, { FLDATAD (WRTACT, tarbell_info_data.FD17XX.writeTrkActive, 0, "Write track active status bit"), }, { FLDATAD (INTRQ, tarbell_info_data.FD17XX.intrq, 0, "INTRQ status bit"), }, { FLDATAD (PROM, tarbell_info_data.promEnabled, 0, "PROM enabled bit"), }, { FLDATAD (WRTPROT, tarbell_info_data.writeProtect, 0, "Write protect enabled bit"), }, { DRDATAD (HDUNLD, tarbell_info_data.headTimeout, 32, "Head unload timeout"), }, { NULL } }; #define TARBELL_NAME "Tarbell SD/DD Floppy Disk Interface" #define TARBELL_SNAME "TARBELL" static const char* tarbell_description(DEVICE *dptr) { return TARBELL_NAME; } #define UNIT_V_TARBELL_VERBOSE (UNIT_V_UF + 0) /* VERBOSE / QUIET */ #define UNIT_TARBELL_VERBOSE (1 << UNIT_V_TARBELL_VERBOSE) #define UNIT_V_TARBELL_WPROTECT (UNIT_V_UF + 1) /* WRTENB / WRTPROT */ #define UNIT_TARBELL_WPROTECT (1 << UNIT_V_TARBELL_WPROTECT) #define UNIT_V_SIO_SLEEP (UNIT_V_UF + 7) /* sleep after keyboard status check */ #define UNIT_SIO_SLEEP (1 << UNIT_V_SIO_SLEEP) static MTAB tarbell_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL, "Sets disk controller I/O base address" }, { MTAB_XTD|MTAB_VDV, 0, "DMABASE", "DMABASE", &tarbell_set_dmabase, &tarbell_show_dmabase, NULL, "Sets disk controller DMA base address" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "PROM", "PROM={ENABLE|DISABLE}", &tarbell_set_prom, &tarbell_show_prom, NULL, "Set/Show PROM enabled/disabled status"}, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MODEL", "MODEL={SD|DD}", &tarbell_set_model, &tarbell_show_model, NULL, "Set/Show the current controller model" }, { UNIT_TARBELL_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, NULL, "No verbose messages for unit " TARBELL_SNAME "n" }, { UNIT_TARBELL_VERBOSE, UNIT_TARBELL_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, NULL, "Verbose messages for unit " TARBELL_SNAME "n" }, { UNIT_TARBELL_WPROTECT, 0, "WRTENB", "WRTENB", NULL, NULL, NULL, "Enables " TARBELL_SNAME "n for writing" }, { UNIT_TARBELL_WPROTECT, UNIT_TARBELL_WPROTECT, "WRTPROT", "WRTPROT", NULL, NULL, NULL, "Protects " TARBELL_SNAME "n from writing" }, { 0 } }; /* 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 RD_DATA_DETAIL_MSG (1 << 6) #define WR_DATA_DETAIL_MSG (1 << 7) /* Debug Flags */ static DEBTAB tarbell_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" }, { "RDDETAIL", RD_DATA_DETAIL_MSG, "Read detail messages" }, { "WRDETAIL", WR_DATA_DETAIL_MSG, "Write detail messags" }, { NULL, 0 } }; DEVICE tarbell_dev = { TARBELL_SNAME, /* name */ tarbell_unit, /* unit */ tarbell_reg, /* registers */ tarbell_mod, /* modifiers */ TARBELL_MAX_DRIVES, /* # units */ 10, /* address radix */ 31, /* address width */ 1, /* addr increment */ TARBELL_MAX_DRIVES, /* data radix */ TARBELL_MAX_DRIVES, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &tarbell_reset, /* reset routine */ &tarbell_boot, /* boot routine */ &tarbell_attach, /* attach routine */ &tarbell_detach, /* detach routine */ &tarbell_info_data, /* context */ (DEV_DISABLE | DEV_DIS | DEV_DEBUG), /* flags */ ERROR_MSG, /* debug control */ tarbell_dt, /* debug flags */ NULL, /* mem size routine */ NULL, /* logical name */ NULL, /* help */ NULL, /* attach help */ NULL, /* context for help */ &tarbell_description /* description */ }; /* Reset routine */ static t_stat tarbell_reset(DEVICE *dptr) { uint8 i; TARBELL_INFO *pInfo = (TARBELL_INFO *)dptr->ctxt; if (dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pInfo->pnp.mem_base, pInfo->pnp.mem_size, RESOURCE_TYPE_MEMORY, &tarbellprom, "tarbellprom", TRUE); sim_map_resource(pInfo->pnp.io_base, pInfo->pnp.io_size, RESOURCE_TYPE_IO, &tarbelldev, "tarbelldev", TRUE); sim_map_resource(pInfo->dma_base, pInfo->dma_size, RESOURCE_TYPE_IO, &tarbelldma, "tarbelldma", TRUE); } else { if (sim_map_resource(pInfo->pnp.mem_base, pInfo->pnp.mem_size, RESOURCE_TYPE_MEMORY, &tarbellprom, "tarbellprom", FALSE) != 0) { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": Error mapping MEM resource at 0x%04x\n", pInfo->pnp.mem_base); return SCPE_ARG; } /* Connect I/O Ports at base address */ if (sim_map_resource(pInfo->pnp.io_base, pInfo->pnp.io_size, RESOURCE_TYPE_IO, &tarbelldev, "tarbelldev", FALSE) != 0) { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": Error mapping I/O resource at 0x%02x\n", pInfo->pnp.io_base); return SCPE_ARG; } /* If simulating a DD, connect DMA ports */ if (tarbell_info->ddEnabled) { if (sim_map_resource(pInfo->dma_base, pInfo->dma_size, RESOURCE_TYPE_IO, &tarbelldma, "tarbelldma", FALSE) != 0) { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": Error mapping DMA resource at 0x%02x\n", pInfo->dma_base); return SCPE_ARG; } } } pInfo->currentDrive = 0; pInfo->promEnabled = TRUE; pInfo->writeProtect = FALSE; /* Reset Registers and Interface Controls */ for (i=0; i < TARBELL_MAX_DRIVES; i++) { if (tarbell_info->uptr[i] == NULL) { tarbell_info->uptr[i] = &tarbell_dev.units[i]; } pInfo->FD17XX.track = 0; pInfo->FD17XX.sector = 1; pInfo->FD17XX.command = 0; pInfo->FD17XX.status = 0; pInfo->FD17XX.data = 0; pInfo->FD17XX.intrq = 0; pInfo->FD17XX.stepDir = 1; pInfo->FD17XX.dataCount = 0; pInfo->FD17XX.trkCount = 0; pInfo->FD17XX.addrActive = FALSE; pInfo->FD17XX.readActive = FALSE; pInfo->FD17XX.readTrkActive = FALSE; pInfo->FD17XX.writeActive = FALSE; pInfo->FD17XX.writeTrkActive = FALSE; pInfo->FD17XX.addrActive = FALSE; tarbell_info->headLoaded[i] = FALSE; tarbell_info->doubleDensity[i] = FALSE; tarbell_info->side[i] = 0; } /* ** If the SIO device sleeps during checks, the SCP usec timer ** is no longer accurate and our wait time has to be adjusted */ pInfo->headTimeout = TARBELL_HEAD_TIMEOUT; /* usec timeout */ sim_debug(STATUS_MSG, &tarbell_dev, TARBELL_SNAME ": reset controller.\n"); return SCPE_OK; } static t_stat tarbell_svc(UNIT *uptr) { FD17XX_REG *pFD17XX; pFD17XX = &tarbell_info->FD17XX; if (tarbell_info->headLoaded[tarbell_info->currentDrive] == TRUE) { TARBELL_HeadLoad(uptr, pFD17XX, FALSE); } return SCPE_OK; } /* Attach routine */ static t_stat tarbell_attach(UNIT *uptr, CONST char *cptr) { char header[4]; t_stat r; unsigned int i = 0; r = attach_unit(uptr, cptr); /* attach unit */ if (r != SCPE_OK) { /* error? */ sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": ATTACH error=%d\n", r); return r; } /* Determine length of this disk */ if (sim_fsize(uptr->fileref) != 0) { uptr->capac = sim_fsize(uptr->fileref); } else { uptr->capac = TARBELL_CAPACITY; } DBG_PRINT(("TARBELL: ATTACH uptr->capac=%d\n", uptr->capac)); for (i = 0; i < TARBELL_MAX_DRIVES; i++) { if (tarbell_dev.units[i].fileref == uptr->fileref) { break; } } if (i >= TARBELL_MAX_DRIVES) { return SCPE_ARG; } /* Default for new file is DSK */ uptr->u3 = IMAGE_TYPE_DSK; if (uptr->capac > 0) { char *rtn = fgets(header, 4, uptr->fileref); if ((rtn != NULL) && (strncmp(header, "CPT", 3) == 0)) { sim_printf("CPT images not yet supported\n"); uptr->u3 = IMAGE_TYPE_CPT; tarbell_detach(uptr); return SCPE_OPENERR; } else { uptr->u3 = IMAGE_TYPE_DSK; } } if (uptr->flags & UNIT_TARBELL_VERBOSE) { sim_printf(TARBELL_SNAME "%d, attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); } return SCPE_OK; } /* Detach routine */ static t_stat tarbell_detach(UNIT *uptr) { t_stat r; int8 i; for (i = 0; i < TARBELL_MAX_DRIVES; i++) { if (tarbell_dev.units[i].fileref == uptr->fileref) { break; } } if (i >= TARBELL_MAX_DRIVES) { return SCPE_ARG; } DBG_PRINT(("Detach TARBELL%d\n", i)); r = detach_unit(uptr); /* detach unit */ if (r != SCPE_OK) { return r; } tarbell_dev.units[i].fileref = NULL; if (uptr->flags & UNIT_TARBELL_VERBOSE) { sim_printf(TARBELL_SNAME "%d detached.\n", i); } return SCPE_OK; } static t_stat tarbell_set_dmabase(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { uint32 newba; t_stat r; if (cptr == NULL || !(tarbell_info->ddEnabled)) { return SCPE_ARG; } newba = get_uint (cptr, 16, 0xFF, &r); if (r != SCPE_OK) { return r; } if ((newba > 0xFF) || (newba % tarbell_info->dma_size)) { return SCPE_ARG; } if (tarbell_dev.flags & DEV_DIS) { sim_printf("device not enabled yet.\n"); tarbell_info->dma_base = newba & ~(tarbell_info->dma_size-1); } else { tarbell_dev.flags |= DEV_DIS; tarbell_reset(&tarbell_dev); tarbell_info->dma_base = newba & ~(tarbell_info->dma_size-1); tarbell_dev.flags &= ~DEV_DIS; tarbell_reset(&tarbell_dev); } return SCPE_OK; } static t_stat tarbell_show_dmabase(FILE *st, UNIT *uptr, int32 val, CONST void *desc) { if (tarbell_info->ddEnabled) { fprintf(st, "DMA=0x%02X-0x%02X", tarbell_info->dma_base, tarbell_info->dma_base+tarbell_info->dma_size-1); } else { fprintf(st, "DMA=N/A"); } return SCPE_OK; } static t_stat tarbell_set_model(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { if (!cptr) return SCPE_IERR; /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "DD")) { tarbell_info->ddEnabled = TRUE; tarbell_info->pnp.io_size = TARBELL_IOSIZE_DD; } else if (!strcmp(cptr, "SD")) { tarbell_info->ddEnabled = FALSE; tarbell_info->pnp.io_size = TARBELL_IOSIZE_SD; } else { return SCPE_ARG; } /* Reset the device if enabled */ if (!(tarbell_dev.flags & DEV_DIS)) { tarbell_dev.flags |= DEV_DIS; tarbell_reset(&tarbell_dev); tarbell_dev.flags &= ~DEV_DIS; tarbell_reset(&tarbell_dev); } return SCPE_OK; } static t_stat tarbell_show_model(FILE *st, UNIT *uptr, int32 val, CONST void *desc) { fprintf(st, "MODEL=%s", (tarbell_info->ddEnabled) ? "DD" : "SD"); return SCPE_OK; } static t_stat tarbell_set_prom(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { if (!cptr) return SCPE_IERR; if (!strlen(cptr)) return SCPE_ARG; /* this assumes that the parameter has already been upcased */ if (!strncmp(cptr, "ENABLE", strlen(cptr))) { tarbell_info->promEnabled = TRUE; } else if (!strncmp(cptr, "DISABLE", strlen(cptr))) { tarbell_info->promEnabled = FALSE; } else { return SCPE_ARG; } return SCPE_OK; } static t_stat tarbell_show_prom(FILE *st, UNIT *uptr, int32 val, CONST void *desc) { fprintf(st, "%s", (tarbell_info->promEnabled) ? "PROM" : "NOPROM"); return SCPE_OK; } static t_stat tarbell_boot(int32 unitno, DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; sim_debug(STATUS_MSG, &tarbell_dev, TARBELL_SNAME ": Booting Controller at 0x%04x\n", pnp->mem_base); *((int32 *) sim_PC->loc) = pnp->mem_base; return SCPE_OK; } static int32 tarbelldev(int32 Addr, int32 rw, int32 data) { if (rw == 0) { /* Read */ return(TARBELL_Read(Addr)); } else { /* Write */ return(TARBELL_Write(Addr, data)); } } static void showdata(int32 isRead) { int32 i; sim_debug(isRead ? RD_DATA_DETAIL_MSG : WR_DATA_DETAIL_MSG, &tarbell_dev, TARBELL_SNAME ": %s track/sector %02d/%03d:\n\t", isRead ? "Read" : "Write", tarbell_info->FD17XX.track, tarbell_info->FD17XX.sector); for (i=0; i < TARBELL_SECTOR_LEN; i++) { sim_debug(isRead ? RD_DATA_DETAIL_MSG : WR_DATA_DETAIL_MSG, &tarbell_dev, "%02X ", sdata[i]); if (((i+1) & 0xf) == 0) { sim_debug(isRead ? RD_DATA_DETAIL_MSG : WR_DATA_DETAIL_MSG, &tarbell_dev, "\n\t"); } } sim_debug(RD_DATA_DETAIL_MSG|WR_DATA_DETAIL_MSG, &tarbell_dev, "\n"); } static uint32 secs_per_track(uint8 track) { /* Track 0 / side 0 is always single density */ int32 secs = (tarbell_info->doubleDensity[tarbell_info->currentDrive] && (tarbell_info->side[tarbell_info->currentDrive] || track > 0)) ? TARBELL_SPT_DD : TARBELL_SPT_SD; tarbell_info->secsPerTrack = secs; return secs; } static uint32 bytes_per_track(uint8 track) { int32 bytes; int32 dd; dd = tarbell_info->doubleDensity[tarbell_info->currentDrive]; if (dd) { bytes = IBM3740_TRK_HDR_LEN + 247 + (TARBELL_SPT_DD * IBM3740_DD_SEC_LEN); } else { bytes = IBM3740_TRK_HDR_LEN + 247 + (TARBELL_SPT_SD * IBM3740_SD_SEC_LEN); } tarbell_info->bytesPerTrack = bytes; return bytes; } static uint32 calculate_tarbell_sec_offset(uint8 track, uint8 sector) { uint32 offset; uint8 ds = tarbell_info->side[tarbell_info->currentDrive]; /* ** Side 0: tracks 0-76 ** Side 1: tracks 77-153 */ if (ds) { track += 77; } /* ** Calculate track offset */ if (track==0) { offset=0; } else { offset=TARBELL_SPT_SD * TARBELL_SECTOR_LEN; /* Track 0 / Side 0 always SD */ offset+=(track-1) * secs_per_track(track) * TARBELL_SECTOR_LEN; /* Track 1-153 */ } /* ** Add sector offset to track offset */ offset += (sector-1)*TARBELL_SECTOR_LEN; DBG_PRINT(("TARBELL: OFFSET drive=%d side=%d den=%d track=%03d sector=%03d\n", tarbell_info->currentDrive, ds, tarbell_info->doubleDensity[tarbell_info->currentDrive], track, sector)); return (offset); } static void TARBELL_HeadLoad(UNIT *uptr, FD17XX_REG *pFD17XX, uint8 load) { /* ** If no disk has been attached, uptr will be NULL - return */ if (uptr == NULL) { return; } if (load) { sim_activate_after_abs(uptr, tarbell_info->headTimeout); /* activate timer */ if (tarbell_info->headLoaded[tarbell_info->currentDrive] == FALSE) { sim_debug(STATUS_MSG, &tarbell_dev, TARBELL_SNAME ": Drive %d head Loaded.\n", tarbell_info->currentDrive); } } if (load == FALSE && tarbell_info->headLoaded[tarbell_info->currentDrive] == TRUE) { sim_cancel(uptr); /* cancel timer */ sim_debug(STATUS_MSG, &tarbell_dev, TARBELL_SNAME ": Drive %d head Unloaded.\n", tarbell_info->currentDrive); } tarbell_info->headLoaded[tarbell_info->currentDrive] = load; } static uint8 TARBELL_Read(uint32 Addr) { uint8 cData; uint8 driveNum; FD17XX_REG *pFD17XX; UNIT *uptr; driveNum = tarbell_info->currentDrive; uptr = tarbell_info->uptr[driveNum]; pFD17XX = &tarbell_info->FD17XX; switch(Addr & 0x07) { case TARBELL_REG_STATUS: cData = pFD17XX->status; break; case TARBELL_REG_TRACK: cData = pFD17XX->track; break; case TARBELL_REG_DATA: /* ** If a READ operation is currently active, get the next byte */ if (pFD17XX->readActive) { /* Store byte in DATA register */ pFD17XX->data = sdata[pFD17XX->dataCount++]; /* If we reached the end of the sector, terminate command and set INTRQ */ if (pFD17XX->dataCount == TARBELL_SECTOR_LEN) { pFD17XX->readActive = FALSE; pFD17XX->dataCount = 0; pFD17XX->status = 0x00; pFD17XX->intrq = TRUE; } else { pFD17XX->status |= FD17XX_STAT_DRQ; /* Another byte is ready */ } TARBELL_HeadLoad(uptr, pFD17XX, TRUE); } else if (pFD17XX->readTrkActive) { /* If we reached the end of the track data, terminate command and set INTRQ */ if (pFD17XX->trkCount == bytes_per_track(pFD17XX->track)) { pFD17XX->readTrkActive = FALSE; pFD17XX->status = 0x00; pFD17XX->intrq = TRUE; } else { pFD17XX->trkCount++; pFD17XX->status |= FD17XX_STAT_DRQ; /* Another byte is ready */ } TARBELL_HeadLoad(uptr, pFD17XX, TRUE); } else if (pFD17XX->addrActive) { /* Store byte in DATA register */ pFD17XX->data = sdata[pFD17XX->dataCount++]; DBG_PRINT(("TARBELL: READ ADDR data=%03d\n", pFD17XX->data)); /* If we reached the end of the address data, terminate command and set INTRQ */ if (pFD17XX->dataCount > TARBELL_ADDR_CRC2) { pFD17XX->addrActive = FALSE; pFD17XX->status = 0x00; pFD17XX->intrq = TRUE; } else { pFD17XX->status |= FD17XX_STAT_DRQ; /* Another byte is ready */ } TARBELL_HeadLoad(uptr, pFD17XX, TRUE); } cData = pFD17XX->data; break; case TARBELL_REG_SECTOR: cData = pFD17XX->sector; break; case TARBELL_REG_WAIT: cData = (pFD17XX->intrq) ? 0x00 : 0x80; /* Bit 7 True if DRQ */ break; case TARBELL_REG_DMASTAT: cData = 0x00; /* Always show DMA is complete */ break; default: sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": READ Invalid I/O Address %02x (%02x)\n", Addr & 0xFF, Addr & 0x07); cData = 0xff; break; } DBG_PRINT(("TARBELL: READ COMPLETE currentDrive=%d doubleDensity=%d sector=%03d track=%02d data=%02x\n", tarbell_info->currentDrive, tarbell_info->doubleDensity[tarbell_info->currentDrive], pFD17XX->track, pFD17XX->sector, pFD17XX->data)); return (cData); } static uint8 TARBELL_Write(uint32 Addr, int32 Data) { uint8 cData; uint8 driveNum; int32 rtn; UNIT *uptr; FD17XX_REG *pFD17XX; sim_debug(CMD_MSG, &tarbell_dev, TARBELL_SNAME ": OUT %02x Data %02x\n", Addr & 0xFF, Data & 0xFF); cData = 0; driveNum = tarbell_info->currentDrive; uptr = tarbell_info->uptr[driveNum]; pFD17XX = &tarbell_info->FD17XX; switch(Addr & 0x07) { case TARBELL_REG_COMMAND: cData = TARBELL_Command(uptr, pFD17XX, Data); break; case TARBELL_REG_DATA: pFD17XX->data = Data; /* Store byte in DATA register */ if (pFD17XX->writeActive) { /* Store DATA register in Sector Buffer */ sdata[pFD17XX->dataCount++] = pFD17XX->data; /* If we reached the end of the sector, write sector, terminate command and set INTRQ */ if (pFD17XX->dataCount == TARBELL_SECTOR_LEN) { pFD17XX->status = 0x00; /* Clear Status Bits */ rtn = TARBELL_WriteSector(uptr, pFD17XX->track, pFD17XX->sector, sdata); showdata(FALSE); if (rtn != TARBELL_SECTOR_LEN) { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": sim_fwrite errno=%d\n", errno); pFD17XX->status |= FD17XX_STAT_WRITEFAULT; } pFD17XX->writeActive = FALSE; pFD17XX->dataCount = 0; pFD17XX->intrq = TRUE; } else { pFD17XX->status |= FD17XX_STAT_DRQ; /* Ready for another byte */ } TARBELL_HeadLoad(uptr, pFD17XX, TRUE); } else if (pFD17XX->writeTrkActive) { if (pFD17XX->dataAddrMrk) { /* Store DATA register in Sector Buffer */ sdata[pFD17XX->dataCount++] = pFD17XX->data; /* If we reached the end of the sector, write sector */ if (pFD17XX->dataCount == TARBELL_SECTOR_LEN) { pFD17XX->status &= ~FD17XX_STAT_WRITEFAULT; /* Clear Status Bit */ rtn = TARBELL_WriteSector(uptr, pFD17XX->track, pFD17XX->sector, sdata); if (rtn != TARBELL_SECTOR_LEN) { pFD17XX->status |= FD17XX_STAT_WRITEFAULT; sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": WRITE ERROR could not write track %03d sector %03d\n", pFD17XX->track, pFD17XX->sector); } DBG_PRINT(("TARBELL: WRITE TRACK drive=%d track=%03d sector=%03d trkcount=%d datacount=%d data=%02X status=%02X\n", driveNum, pFD17XX->track, pFD17XX->sector, pFD17XX->trkCount, pFD17XX->dataCount, pFD17XX->data, pFD17XX->status)); pFD17XX->dataCount = 0; pFD17XX->dataAddrMrk = 0; if (pFD17XX->sector < secs_per_track(pFD17XX->track)) { pFD17XX->sector++; } } } else if (pFD17XX->data == 0xFB) { pFD17XX->dataAddrMrk = 1; DBG_PRINT(("TARBELL: 0xFB Address Mark Found\n")); } /* ** Increment number for bytes written to track */ pFD17XX->trkCount++; if (pFD17XX->trkCount < bytes_per_track(pFD17XX->track)) { pFD17XX->status |= FD17XX_STAT_DRQ; /* Ready for another byte */ } else { pFD17XX->status = 0x00; /* Clear Status Bits */ pFD17XX->intrq = TRUE; pFD17XX->status &= ~FD17XX_STAT_BUSY; /* Clear BUSY Bit */ pFD17XX->writeTrkActive = FALSE; sim_debug(WR_DATA_MSG, &tarbell_dev, TARBELL_SNAME ": WRITE TRACK COMPLETE track=%03d sector=%03d trkcount=%d datacount=%d data=%02X status=%02X\n", pFD17XX->track, pFD17XX->sector, pFD17XX->trkCount, pFD17XX->dataCount, pFD17XX->data, pFD17XX->status); } TARBELL_HeadLoad(uptr, pFD17XX, TRUE); } DBG_PRINT(("TARBELL: WRITE DATA REG %02X\n", Data)); break; case TARBELL_REG_TRACK: pFD17XX->track = Data; DBG_PRINT(("TARBELL: TRACK REG=%d\n", pFD17XX->track)); break; case TARBELL_REG_SECTOR: pFD17XX->sector = Data; DBG_PRINT(("TARBELL: SECTOR REG=%d\n", pFD17XX->sector)); break; case TARBELL_REG_DRVSEL: /* Drive Select */ if (tarbell_info->ddEnabled) { /* Tarbell DD Controller */ cData = (Data & TARBELL_DSEL_MASK) >> 4; /* Density */ tarbell_info->doubleDensity[cData] = (Data & TARBELL_DENS_MASK) != 0x00; /* Side */ tarbell_info->side[cData] = (Data & TARBELL_SIDE_MASK) != 0x00; DBG_PRINT(("REG_DRVSEL=%02X\n", Data)); } else { cData = ~(Data >> 4) & 0x03; /* Tarbell SD drive bits inverted */ } if (tarbell_info->currentDrive != cData) { sim_debug(STATUS_MSG, &tarbell_dev, TARBELL_SNAME ": Current drive now %d\n", cData); } tarbell_info->currentDrive = cData; break; case TARBELL_REG_EXTADDR: cData = 0x00; break; default: sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": WRITE Invalid I/O Address %02x (%02x)\n", Addr & 0xFF, Addr & 0x07); cData = 0xff; break; } DBG_PRINT(("TARBELL: WRITE COMPLETE currentDrive=%d doubleDensity=%d sector=%03d track=%02d data=%02x\n", tarbell_info->currentDrive, tarbell_info->doubleDensity[tarbell_info->currentDrive], pFD17XX->track, pFD17XX->sector, pFD17XX->data)); return(cData); } static uint32 TARBELL_ReadSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer) { uint32 sec_offset; uint32 rtn = 0; if (uptr->fileref == NULL) { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": READSEC uptr.fileref is NULL!\n"); return 0; } sec_offset = calculate_tarbell_sec_offset(track, sector); sim_debug(RD_DATA_MSG, &tarbell_dev, TARBELL_SNAME ": READSEC track %03d sector %03d at offset %04X\n", track, sector, sec_offset); if (sim_fseek(uptr->fileref, sec_offset, SEEK_SET) != 0) { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": READSEC sim_fseek error.\n"); return 0; } rtn = sim_fread(buffer, 1, TARBELL_SECTOR_LEN, uptr->fileref); return rtn; } static uint32 TARBELL_WriteSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer) { uint32 sec_offset; uint32 rtn = 0; if (uptr->fileref == NULL) { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": READSEC uptr.fileref is NULL!\n"); return 0; } sec_offset = calculate_tarbell_sec_offset(track, sector); sim_debug(WR_DATA_MSG, &tarbell_dev, TARBELL_SNAME ": WRITESEC track %03d sector %03d at offset %04X\n", track, sector, sec_offset); if (sim_fseek(uptr->fileref, sec_offset, SEEK_SET) != 0) { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": WRITESEC sim_fseek error.\n"); return 0; } rtn = sim_fwrite(buffer, 1, TARBELL_SECTOR_LEN, uptr->fileref); return rtn; } static uint8 TARBELL_Command(UNIT *uptr, FD17XX_REG *pFD17XX, int32 Data) { uint8 cData; uint8 newTrack; uint8 statusUpdate; int32 rtn; cData = 0; statusUpdate = TRUE; if (uptr == NULL) { return cData; } pFD17XX->command = Data; /* ** Type II-IV Command */ if (pFD17XX->command & 0x80) { pFD17XX->readActive = FALSE; pFD17XX->writeActive = FALSE; pFD17XX->readTrkActive = FALSE; pFD17XX->writeTrkActive = FALSE; pFD17XX->addrActive = FALSE; pFD17XX->dataCount = 0; pFD17XX->status &= ~FD17XX_STAT_DRQ; /* Reset DRQ */ } /* ** Set BUSY for all but Force Interrupt */ if ((pFD17XX->command & TARBELL_CMD_FORCE_INTR) != TARBELL_CMD_FORCE_INTR) { pFD17XX->status |= FD17XX_STAT_BUSY; } pFD17XX->intrq = FALSE; switch(pFD17XX->command & 0xf0) { case TARBELL_CMD_RESTORE: pFD17XX->track = 0; sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": RESTORE track=%03d\n", pFD17XX->track); TARBELL_HeadLoad(uptr, pFD17XX, (Data & TARBELL_FLAG_H) ? TRUE : FALSE); pFD17XX->status &= ~FD17XX_STAT_SEEKERROR; pFD17XX->status &= ~FD17XX_STAT_BUSY; pFD17XX->status &= ~FD17XX_STAT_DRQ; pFD17XX->intrq = TRUE; break; case TARBELL_CMD_SEEK: newTrack = pFD17XX->data; DBG_PRINT(("TARBELL: TRACK DATA=%d (SEEK)\n", newTrack)); pFD17XX->status &= ~FD17XX_STAT_SEEKERROR; if (newTrack < TARBELL_TRACKS) { pFD17XX->track = newTrack; TARBELL_HeadLoad(uptr, pFD17XX, (Data & TARBELL_FLAG_H) ? TRUE : FALSE); sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": SEEK track=%03d\n", pFD17XX->track); } else { pFD17XX->status |= FD17XX_STAT_SEEKERROR; sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": SEEK ERR track=%03d\n", newTrack); } pFD17XX->status &= ~FD17XX_STAT_BUSY; pFD17XX->status &= ~FD17XX_STAT_DRQ; pFD17XX->intrq = TRUE; break; case TARBELL_CMD_STEP: case TARBELL_CMD_STEPU: pFD17XX->status &= ~FD17XX_STAT_SEEKERROR; newTrack = pFD17XX->track + pFD17XX->stepDir; if (newTrack < TARBELL_TRACKS) { if (Data & TARBELL_FLAG_U) { pFD17XX->track = newTrack; } sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": STEP track=%03d\n", pFD17XX->track); } else { pFD17XX->status |= FD17XX_STAT_SEEKERROR; sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": STEP ERR track=%03d\n", newTrack); } TARBELL_HeadLoad(uptr, pFD17XX, (Data & TARBELL_FLAG_H) ? TRUE : FALSE); pFD17XX->status &= ~FD17XX_STAT_BUSY; pFD17XX->status &= ~FD17XX_STAT_DRQ; pFD17XX->intrq = TRUE; break; case TARBELL_CMD_STEPIN: case TARBELL_CMD_STEPINU: pFD17XX->status &= ~FD17XX_STAT_SEEKERROR; if (pFD17XX->track < TARBELL_TRACKS-1) { if (Data & TARBELL_FLAG_U) { pFD17XX->track++; } TARBELL_HeadLoad(uptr, pFD17XX, (Data & TARBELL_FLAG_H) ? TRUE : FALSE); sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": STEPIN track=%03d\n", pFD17XX->track); } else { pFD17XX->status |= FD17XX_STAT_SEEKERROR; sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": STEPIN ERR track=%03d\n", pFD17XX->track+1); } pFD17XX->stepDir = 1; pFD17XX->status &= ~FD17XX_STAT_BUSY; pFD17XX->status &= ~FD17XX_STAT_DRQ; pFD17XX->intrq = TRUE; break; case TARBELL_CMD_STEPOUT: case TARBELL_CMD_STEPOUTU: pFD17XX->status &= ~FD17XX_STAT_SEEKERROR; if (pFD17XX->track > 0) { if (Data & TARBELL_FLAG_U) { pFD17XX->track--; } TARBELL_HeadLoad(uptr, pFD17XX, (Data & TARBELL_FLAG_H) ? TRUE : FALSE); sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": STEPOUT track=%03d\n", pFD17XX->track); } else { pFD17XX->status |= FD17XX_STAT_SEEKERROR; sim_debug(SEEK_MSG, &tarbell_dev, TARBELL_SNAME ": STEPOUT ERR track=%03d\n", pFD17XX->track-1); } pFD17XX->stepDir = -1; pFD17XX->status &= ~FD17XX_STAT_BUSY; pFD17XX->status &= ~FD17XX_STAT_DRQ; pFD17XX->intrq = TRUE; break; case TARBELL_CMD_READ: if ((uptr == NULL) || (uptr->fileref == NULL)) { sim_debug(STATUS_MSG, &tarbell_dev, TARBELL_SNAME ": " ADDRESS_FORMAT " Drive: %d not attached - read ignored.\n", PCX, tarbell_info->currentDrive); pFD17XX->status &= ~FD17XX_STAT_BUSY; return cData; } rtn = TARBELL_ReadSector(uptr, pFD17XX->track, pFD17XX->sector, sdata); if (rtn == TARBELL_SECTOR_LEN) { pFD17XX->readActive = TRUE; showdata(TRUE); } else { sim_debug(ERROR_MSG, &tarbell_dev, TARBELL_SNAME ": sim_fread errno=%d\n", errno); pFD17XX->status |= FD17XX_STAT_NOTFOUND; pFD17XX->intrq = TRUE; } break; case TARBELL_CMD_WRITE: /* ** If no disk in drive, return */ if ((uptr == NULL) || (uptr->fileref == NULL)) { sim_debug(STATUS_MSG, &tarbell_dev, TARBELL_SNAME ": " ADDRESS_FORMAT " Drive: %d not attached - write ignored.\n", PCX, tarbell_info->currentDrive); pFD17XX->status &= ~FD17XX_STAT_BUSY; } if ((uptr->flags & UNIT_TARBELL_WPROTECT) || tarbell_info->writeProtect) { DBG_PRINT((TARBELL_SNAME ": Disk write protected. uptr->flags=%04x writeProtect=%04x\n", uptr->flags & UNIT_TARBELL_WPROTECT, tarbell_info->writeProtect)); pFD17XX->intrq = TRUE; } else { pFD17XX->writeActive = TRUE; pFD17XX->dataCount = 0; pFD17XX->status |= FD17XX_STAT_DRQ; /* Set DRQ */ } break; case TARBELL_CMD_READ_ADDRESS: sdata[TARBELL_ADDR_TRACK]=pFD17XX->track; sdata[TARBELL_ADDR_ZEROS]=0; sdata[TARBELL_ADDR_SECTOR]=pFD17XX->sector; sdata[TARBELL_ADDR_LENGTH]=TARBELL_SECTOR_LEN; sdata[TARBELL_ADDR_CRC1]=0; sdata[TARBELL_ADDR_CRC2]=0; pFD17XX->addrActive = TRUE; pFD17XX->status |= FD17XX_STAT_DRQ; /* Set DRQ */ break; case TARBELL_CMD_READ_TRACK: pFD17XX->readTrkActive = TRUE; pFD17XX->trkCount=0; pFD17XX->dataCount=0; pFD17XX->sector=1; pFD17XX->status |= FD17XX_STAT_DRQ; /* Set DRQ */ break; case TARBELL_CMD_WRITE_TRACK: if ((uptr->flags & UNIT_TARBELL_WPROTECT) || tarbell_info->writeProtect) { DBG_PRINT((TARBELL_SNAME ": Disk write protected. uptr->flags=%04x writeProtect=%04x\n", uptr->flags & UNIT_TARBELL_WPROTECT, tarbell_info->writeProtect)); pFD17XX->intrq = TRUE; } else { pFD17XX->writeTrkActive = TRUE; pFD17XX->trkCount=0; pFD17XX->dataCount=0; pFD17XX->sector=1; pFD17XX->dataAddrMrk=0; pFD17XX->status |= FD17XX_STAT_DRQ; /* Set DRQ */ } break; case TARBELL_CMD_FORCE_INTR: if (pFD17XX->status & FD17XX_STAT_BUSY) { pFD17XX->status &= ~FD17XX_STAT_BUSY; statusUpdate = FALSE; } /* Reset Status */ pFD17XX->dataCount = 0; pFD17XX->trkCount = 0; pFD17XX->readActive = FALSE; pFD17XX->readTrkActive = FALSE; pFD17XX->writeActive = FALSE; pFD17XX->writeTrkActive = FALSE; pFD17XX->addrActive = FALSE; break; default: cData=0xFF; sim_debug(ERROR_MSG, &tarbell_dev, "TARBELL: UNRECOGNIZED CMD %02X\n", pFD17XX->command); pFD17XX->intrq = TRUE; break; } /**************************/ /* Update Status Register */ /**************************/ /* drive not ready bit */ pFD17XX->status &= ~FD17XX_STAT_NOTREADY; pFD17XX->status |= (uptr->fileref == NULL) ? FD17XX_STAT_NOTREADY : 0x00; switch(pFD17XX->command & 0xf0) { case TARBELL_CMD_RESTORE: case TARBELL_CMD_SEEK: case TARBELL_CMD_STEP: case TARBELL_CMD_STEPU: case TARBELL_CMD_STEPIN: case TARBELL_CMD_STEPINU: case TARBELL_CMD_STEPOUT: case TARBELL_CMD_STEPOUTU: case TARBELL_CMD_FORCE_INTR: if (statusUpdate) { pFD17XX->status &= ~FD17XX_STAT_HEADLOAD; pFD17XX->status &= ~FD17XX_STAT_WRITEPROT; pFD17XX->status &= ~FD17XX_STAT_CRCERROR; pFD17XX->status &= ~FD17XX_STAT_TRACK0; pFD17XX->status |= ((uptr->flags & UNIT_TARBELL_WPROTECT) || tarbell_info->writeProtect) ? FD17XX_STAT_WRITEPROT : 0x00; pFD17XX->status |= (pFD17XX->track) ? 0x00 : FD17XX_STAT_TRACK0; pFD17XX->status |= (tarbell_info->headLoaded[tarbell_info->currentDrive]) ? FD17XX_STAT_HEADLOAD : 0x00; pFD17XX->status |= (pFD17XX->status & FD17XX_STAT_NOTREADY) ? 0x00 : FD17XX_STAT_INDEX; /* Always set Index Flag if Drive Ready */ } break; case TARBELL_CMD_READ: pFD17XX->status &= ~FD17XX_STAT_LOSTDATA; pFD17XX->status &= ~FD17XX_STAT_NOTFOUND; pFD17XX->status &= ~FD17XX_STAT_CRCERROR; pFD17XX->status &= ~FD17XX_STAT_RTYPELSB; break; case TARBELL_CMD_WRITE: pFD17XX->status &= ~FD17XX_STAT_WRITEPROT; pFD17XX->status &= ~FD17XX_STAT_LOSTDATA; pFD17XX->status &= ~FD17XX_STAT_NOTFOUND; pFD17XX->status &= ~FD17XX_STAT_CRCERROR; pFD17XX->status &= ~FD17XX_STAT_RTYPELSB; pFD17XX->status |= ((uptr->flags & UNIT_TARBELL_WPROTECT) || tarbell_info->writeProtect) ? FD17XX_STAT_WRITEPROT : 0x00; break; case TARBELL_CMD_READ_ADDRESS: pFD17XX->status &= ~0x20; pFD17XX->status &= ~0x40; pFD17XX->status &= ~FD17XX_STAT_LOSTDATA; pFD17XX->status &= ~FD17XX_STAT_NOTFOUND; pFD17XX->status &= ~FD17XX_STAT_CRCERROR; break; case TARBELL_CMD_READ_TRACK: pFD17XX->status &= ~0x08; pFD17XX->status &= ~0x10; pFD17XX->status &= ~0x20; pFD17XX->status &= ~0x40; pFD17XX->status &= ~FD17XX_STAT_LOSTDATA; break; case TARBELL_CMD_WRITE_TRACK: pFD17XX->status &= ~0x08; pFD17XX->status &= ~0x10; pFD17XX->status &= ~FD17XX_STAT_WRITEPROT; pFD17XX->status &= ~FD17XX_STAT_LOSTDATA; pFD17XX->status |= ((uptr->flags & UNIT_TARBELL_WPROTECT) || tarbell_info->writeProtect) ? FD17XX_STAT_WRITEPROT : 0x00; break; } sim_debug(CMD_MSG, &tarbell_dev, TARBELL_SNAME ": CMD cmd=%02X drive=%d side=%d track=%03d sector=%03d status=%02X\n", pFD17XX->command, tarbell_info->currentDrive, tarbell_info->side[tarbell_info->currentDrive], pFD17XX->track, pFD17XX->sector, pFD17XX->status); return(cData); } static int32 tarbellprom(int32 Addr, int32 rw, int32 Data) { /* ** The Tarbell controller overlays the first 32 bytes of RAM with a PROM. ** The PROM is enabled/disabled with switch position 7: ** ON = ENABLED ** OFF = DISABLED ** ** If the PROM is enabled, writes to 0x0000-0x001F are written to RAM, reads are ** from the PROM. ** ** The PROM is disabled if the controller detects a memory read with bit A5 enabled, ** which can't be implemented because Examine reads 6 bytes at a time. We will disable ** the PROM if address >= 0x0025 is read. Hack. */ if (rw == TARBELL_PROM_WRITE) { tarbell_ram[Addr & TARBELL_RAM_MASK] = Data; return 0; } else { if (Addr >= 0x0025 && tarbell_info->promEnabled == TRUE) { tarbell_info->promEnabled = FALSE; sim_debug(STATUS_MSG, &tarbell_dev, TARBELL_SNAME ": Boot PROM disabled.\n"); } if (tarbell_info->promEnabled == TRUE && Addr < TARBELL_PROM_SIZE) { return(tarbell_prom[Addr & TARBELL_PROM_MASK]); } else { return(tarbell_ram[Addr & TARBELL_RAM_MASK]); } } } static int32 tarbelldma(int32 Addr, int32 rw, int32 data) { return 0x00; /* DMA is not implemented */ }