/************************************************************************* * * * Copyright (c) 2007-2023 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 NON- * * INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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 names of The Authors shall * * not be used in advertising or otherwise to promote the sale, use or * * other dealings in this Software without prior written authorization * * from the Authors. * * * * Module Description: * * ImageDisk Disk Image File access module for SIMH, definitions. * * See: http://dunfield.classiccmp.org/img/index.htm * * for details on the ImageDisk format and other utilities. * * * *************************************************************************/ /* Change log: - 06-Aug-2008, Tony Nicholson, Add support for logical Head and Cylinder maps in the .IMD image file (AGN) */ #include "sim_defs.h" #include "sim_imd.h" static t_stat commentParse(DISK_INFO *myDisk, uint8 comment[], uint32 buffLen); static t_stat diskParse(DISK_INFO *myDisk, uint32 isVerbose); static t_stat diskFormat(DISK_INFO *myDisk); /* Open an existing IMD disk image. It will be opened and parsed, and after this * call, will be ready for sector read/write. The result is the corresponding * DISK_INFO or NULL if an error occurred. */ DISK_INFO *diskOpenEx(FILE *fileref, uint32 isVerbose, DEVICE *device, uint32 debugmask, uint32 verbosedebugmask) { DISK_INFO *myDisk = NULL; myDisk = (DISK_INFO*)calloc(1, sizeof(DISK_INFO)); if (myDisk == NULL) { sim_printf("%s: %s(): memory allocation failure.\n", __FILE__, __FUNCTION__); return NULL; } myDisk->file = fileref; myDisk->device = device; myDisk->debugmask = debugmask; myDisk->verbosedebugmask = verbosedebugmask; if (diskParse(myDisk, isVerbose) != SCPE_OK) { free(myDisk); myDisk = NULL; } return myDisk; } DISK_INFO *diskOpen(FILE *fileref, uint32 isVerbose) { return diskOpenEx(fileref, isVerbose, NULL, 0, 0); } /* Scans the IMD file for the comment string, and returns it in comment buffer. * After this function returns, the file pointer is placed after the comment and * the 0x1A "EOF" marker. * * The comment parameter is optional, and if NULL, then the comment will not * be extracted from the IMD file, but the file position will still be advanced * to the end of the comment. */ static t_stat commentParse(DISK_INFO *myDisk, uint8 comment[], uint32 buffLen) { uint8 cData; uint32 commentLen = 0; /* rewind to the beginning of the file. */ rewind(myDisk->file); cData = (uint8)fgetc(myDisk->file); while ((!feof(myDisk->file)) && (cData != 0x1a)) { if ((comment != NULL) && (commentLen < buffLen)) { comment[commentLen++] = cData; } cData = (uint8)fgetc(myDisk->file); } if (comment != NULL) { if (commentLen == buffLen) commentLen--; comment[commentLen] = 0; } return SCPE_OK; } static uint32 headerOk(IMD_HEADER imd) { return (imd.cyl < MAX_CYL) && (imd.head < MAX_HEAD); } /* Parse an IMD image. This sets up sim_imd to be able to do sector read/write and * track write. */ static t_stat diskParse(DISK_INFO *myDisk, uint32 isVerbose) { uint8 comment[256]; uint8 sectorMap[256]; uint8 sectorHeadMap[256]; uint8 sectorCylMap[256]; uint32 sectorSize, sectorHeadwithFlags, sectRecordType; size_t hdrBytes; uint32 i; uint8 start_sect; uint32 TotalSectorCount = 0; IMD_HEADER imd; if(myDisk == NULL) { return (SCPE_OPENERR); } memset(myDisk->track, 0, (sizeof(TRACK_INFO)*MAX_CYL*MAX_HEAD)); if (commentParse(myDisk, comment, sizeof(comment)) != SCPE_OK) { return (SCPE_OPENERR); } if(isVerbose) sim_printf("%s\n", comment); myDisk->nsides = 1; myDisk->ntracks = 0; myDisk->flags = 0; /* Make sure all flags are clear. */ if(feof(myDisk->file)) { sim_printf("SIM_IMD: Disk image is blank, it must be formatted.\n"); return (SCPE_OPENERR); } do { sim_debug(myDisk->debugmask, myDisk->device, "start of track %d at file offset %ld\n", myDisk->ntracks, ftell(myDisk->file)); hdrBytes = sim_fread(&imd, 1, 5, myDisk->file); if ((hdrBytes == 0) && feof(myDisk->file)) break; /* detected end of IMD file */ if (hdrBytes != 5) { sim_printf("SIM_IMD: Header read returned %" SIZE_T_FMT "u bytes instead of 5.\n", hdrBytes); return (SCPE_OPENERR); } if (feof(myDisk->file)) break; sectorSize = 128 << (imd.sectsize & 0x1f); sectorHeadwithFlags = imd.head; /*AGN save the head and flags */ imd.head &= 1 ; /*AGN mask out flag bits to head 0 or 1 */ sim_debug(myDisk->debugmask, myDisk->device, "Track %d:\n", myDisk->ntracks); sim_debug(myDisk->debugmask, myDisk->device, "\tMode=%d, Cyl=%d, Head=%d(%d), #sectors=%d, sectsize=%d (%d bytes)\n", imd.mode, imd.cyl, sectorHeadwithFlags, imd.head, imd.nsects, imd.sectsize, sectorSize); if (!headerOk(imd)) { sim_printf("SIM_IMD: Corrupt header.\n"); return (SCPE_OPENERR); } if((imd.head + 1) > myDisk->nsides) { myDisk->nsides = imd.head + 1; } if (imd.head >= MAX_HEAD) { sim_printf("SIM_IMD: Invalid head %d, a maximum of %d heads are supported.\n", imd.head, MAX_HEAD); return (SCPE_IERR); } if (imd.cyl >= MAX_CYL) { sim_printf("SIM_IMD: Invalid cylinder %d, a maximum of %d cylinders are supported.\n", imd.cyl, MAX_CYL); return (SCPE_IERR); } myDisk->track[imd.cyl][imd.head].mode = imd.mode; myDisk->track[imd.cyl][imd.head].nsects = imd.nsects; myDisk->track[imd.cyl][imd.head].sectsize = sectorSize; if (sim_fread(sectorMap, 1, imd.nsects, myDisk->file) != imd.nsects) { sim_printf("SIM_IMD: Corrupt file [Sector Map].\n"); return (SCPE_OPENERR); } myDisk->track[imd.cyl][imd.head].start_sector = imd.nsects; sim_debug(myDisk->debugmask, myDisk->device, "\tSector Map: "); for(i=0;idebugmask, myDisk->device, "%d ", sectorMap[i]); if(sectorMap[i] < myDisk->track[imd.cyl][imd.head].start_sector) { myDisk->track[imd.cyl][imd.head].start_sector = sectorMap[i]; } } sim_debug(myDisk->debugmask, myDisk->device, ", Start Sector=%d", myDisk->track[imd.cyl][imd.head].start_sector); if(sectorHeadwithFlags & IMD_FLAG_SECT_HEAD_MAP) { if (sim_fread(sectorHeadMap, 1, imd.nsects, myDisk->file) != imd.nsects) { sim_printf("SIM_IMD: Corrupt file [Sector Head Map].\n"); return (SCPE_OPENERR); } sim_debug(myDisk->debugmask, myDisk->device, "\tSector Head Map: "); for(i=0;idebugmask, myDisk->device, "%d ", sectorHeadMap[i]); } sim_debug(myDisk->debugmask, myDisk->device, "\n"); } else { /* Default Head is physical head for each sector */ for(i=0;ifile) != imd.nsects) { sim_printf("SIM_IMD: Corrupt file [Sector Cyl Map].\n"); return (SCPE_OPENERR); } sim_debug(myDisk->debugmask, myDisk->device, "\tSector Cyl Map: "); for(i=0;idebugmask, myDisk->device, "%d ", sectorCylMap[i]); } sim_debug(myDisk->debugmask, myDisk->device, "\n"); } else { /* Default Cyl Map is physical cylinder for each sector */ for(i=0;idebugmask, myDisk->device, "\nSector data at offset 0x%08lx\n", ftell(myDisk->file)); /* Build the table with location 0 being the start sector. */ start_sect = myDisk->track[imd.cyl][imd.head].start_sector; /* Now read each sector */ for(i=0;idebugmask, myDisk->device, "Sector Phys: %2d/Logical: %2d: %4d bytes, offset: 0x%05x: ", i, sectorMap[i], sectorSize, (unsigned int)ftell(myDisk->file)); sectRecordType = fgetc(myDisk->file); /* AGN Logical head mapping */ myDisk->track[imd.cyl][imd.head].logicalHead[i] = sectorHeadMap[i]; /* AGN Logical cylinder mapping */ myDisk->track[imd.cyl][imd.head].logicalCyl[i] = sectorCylMap[i]; switch(sectRecordType) { case SECT_RECORD_UNAVAILABLE: /* Data could not be read from the original media */ if (sectorMap[i]-start_sect < MAX_SPT) myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = 0xBADBAD; else { sim_printf("SIM_IMD: ERROR: Illegal sector offset %d\n", sectorMap[i]-start_sect); return (SCPE_OPENERR); } break; case SECT_RECORD_NORM: /* Normal Data */ case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ case SECT_RECORD_NORM_ERR: /* Normal Data with read error */ case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ /* sim_debug(myDisk->debugmask, myDisk->device, "Uncompressed Data\n"); */ if (sectorMap[i]-start_sect < MAX_SPT) { myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = ftell(myDisk->file); (void)sim_fseek(myDisk->file, sectorSize, SEEK_CUR); } else { sim_printf("SIM_IMD: ERROR: Illegal sector offset %d\n", sectorMap[i]-start_sect); return (SCPE_OPENERR); } break; case SECT_RECORD_NORM_COMP: /* Compressed Normal Data */ case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ case SECT_RECORD_NORM_COMP_ERR: /* Compressed Normal Data */ case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ if (sectorMap[i]-start_sect < MAX_SPT) { myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = ftell(myDisk->file); myDisk->flags |= FD_FLAG_WRITELOCK; /* Write-protect the disk if any sectors are compressed. */ if (1) { uint8 cdata = (uint8)fgetc(myDisk->file); sim_debug(myDisk->debugmask, myDisk->device, "Compressed Data = 0x%02x", cdata); } } else { sim_printf("SIM_IMD: ERROR: Illegal sector offset %d\n", sectorMap[i]-start_sect); return (SCPE_OPENERR); } break; default: sim_printf("SIM_IMD: ERROR: unrecognized sector record type %d\n", sectRecordType); return (SCPE_OPENERR); break; } sim_debug(myDisk->debugmask, myDisk->device, "\n"); } myDisk->ntracks++; } while (!feof(myDisk->file)); sim_debug(myDisk->debugmask, myDisk->device, "Processed %d sectors\n", TotalSectorCount); for(i=0;intracks;i++) { uint8 j; sim_debug(myDisk->verbosedebugmask, myDisk->device, "Track %3d: ", i); for(j=0;jtrack[i >> 1][i & 1].nsects;j++) { sim_debug(myDisk->verbosedebugmask, myDisk->device, "0x%05x ", myDisk->track[i >> 1][i & 1].sectorOffsetMap[j]); } sim_debug(myDisk->verbosedebugmask, myDisk->device, "\n"); } if(myDisk->flags & FD_FLAG_WRITELOCK) { sim_printf("Disk write-protected because the image contains compressed sectors. Use IMDU to uncompress.\n"); } return SCPE_OK; } /* * This function closes the IMD image. After closing, the sector read/write operations are not * possible. * * The IMD file is not actually closed, we leave that to SIMH. */ t_stat diskClose(DISK_INFO **myDisk) { if(*myDisk == NULL) return SCPE_OPENERR; free(*myDisk); *myDisk = NULL; return SCPE_OK; } #define MAX_COMMENT_LEN 256 /* * Create an ImageDisk (IMD) file. This function just creates the comment header, and allows * the user to enter a comment. After the IMD is created, it must be formatted with a format * program on the simulated operating system, ie CP/M, CDOS, 86-DOS. * * If the IMD file already exists, the user will be given the option of overwriting it. */ t_stat diskCreate(FILE *fileref, const char *ctlr_comment) { DISK_INFO *myDisk = NULL; char *comment; char *curptr; char *result; uint8 answer; size_t len; size_t remaining; if(fileref == NULL) { return (SCPE_OPENERR); } if(sim_fsize(fileref) != 0) { sim_printf("SIM_IMD: Disk image already has data, do you want to overwrite it? "); answer = (uint8)getchar(); if((answer != 'y') && (answer != 'Y')) { return (SCPE_OPENERR); } } if((curptr = comment = (char *)calloc(1, MAX_COMMENT_LEN)) == 0) { sim_printf("Memory allocation failure.\n"); return (SCPE_MEM); } sim_printf("SIM_IMD: Enter a comment for this disk.\n" "SIM_IMD: Terminate with a '.' on an otherwise blank line.\n"); remaining = MAX_COMMENT_LEN; do { sim_printf("IMD> "); /* ISO C says that the 2nd argument is an int, not size_t. */ result = fgets(curptr, (int) (remaining - 3), stdin); if ((result == NULL) || (strcmp(curptr, ".\n") == 0)) { remaining = 0; } else { len = strlen(curptr) - 1; if (curptr[len] != '\n') len++; remaining -= len; curptr += len; *curptr++ = 0x0d; *curptr++ = 0x0a; } } while (remaining > 4); *curptr = 0x00; /* rewind to the beginning of the file. */ rewind(fileref); /* Erase the contents of the IMD file in case we are overwriting an existing image. */ if (sim_set_fsize(fileref, 0) == -1) { free(comment); sim_printf("SIM_IMD: Error overwriting disk image.\n"); return(SCPE_OPENERR); } fprintf(fileref, "IMD SIMH %s %s\n", __DATE__, __TIME__); fputs(comment, fileref); free(comment); fprintf(fileref, "\n\n$Id: sim_imd.c 1999 2008-07-22 04:25:28Z hharte $\n"); fprintf(fileref, "%s\n", ctlr_comment); fputc(0x1A, fileref); /* EOF marker for IMD comment. */ fflush(fileref); if((myDisk = diskOpen(fileref, 0)) == NULL) { sim_printf("SIM_IMD: Error opening disk for format.\n"); return(SCPE_OPENERR); } if(diskFormat(myDisk) != SCPE_OK) { sim_printf("SIM_IMD: error formatting disk.\n"); } return diskClose(&myDisk); } static t_stat diskFormat(DISK_INFO *myDisk) { uint8 i; uint8 sector_map[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}; uint32 flags; sim_printf("SIM_IMD: Formatting disk in IBM 3740 SS/SD Format.\n"); for(i=0;i<77;i++) { if((trackWrite(myDisk, i, 0, 26, 128, sector_map, IMD_MODE_500K_FM, 0xE5, &flags)) != 0) { sim_printf("SIM_IMD: Error formatting track %d\n", i); return SCPE_IOERR; } else { putchar('.'); } } sim_printf("\nSIM_IMD: Format Complete.\n"); return SCPE_OK; } uint32 imdGetSides(DISK_INFO *myDisk) { if(myDisk != NULL) { return(myDisk->nsides); } return (0); } uint32 imdIsWriteLocked(DISK_INFO *myDisk) { if(myDisk != NULL) { return((myDisk->flags & FD_FLAG_WRITELOCK) ? 1 : 0); } return (0); } /* Check that the given track/sector exists on the disk */ t_stat sectSeek(DISK_INFO *myDisk, uint32 Cyl, uint32 Head) { if(Cyl >= myDisk->ntracks) { return(SCPE_IOERR); } if(Head >= myDisk->nsides) { return(SCPE_IOERR); } if(myDisk->track[Cyl][Head].nsects == 0) { sim_debug(myDisk->debugmask, myDisk->device, "%s: invalid track/head\n", __FUNCTION__); return(SCPE_IOERR); } return(SCPE_OK); } /* Read a sector from an IMD image. */ t_stat sectRead(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 Sector, uint8 *buf, uint32 buflen, uint32 *flags, uint32 *readlen) { uint32 sectorFileOffset; uint8 sectRecordType; uint8 start_sect; *readlen = 0; *flags = 0; /* Check parameters */ if(myDisk == NULL) { *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(sectSeek(myDisk, Cyl, Head) != SCPE_OK) { *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(Sector > myDisk->track[Cyl][Head].nsects) { sim_debug(myDisk->debugmask, myDisk->device, "%s: invalid sector\n", __FUNCTION__); *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(buflen < myDisk->track[Cyl][Head].sectsize) { sim_printf("%s: Reading C:%d/H:%d/S:%d, len=%d: user buffer too short, need %d\n", __FUNCTION__, Cyl, Head, Sector, buflen, myDisk->track[Cyl][Head].sectsize); *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } start_sect = myDisk->track[Cyl][Head].start_sector; sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect]; sim_debug(myDisk->debugmask, myDisk->device, "Reading C:%d/H:%d/S:%d, len=%d, offset=0x%08x\n", Cyl, Head, Sector, buflen, sectorFileOffset); (void)sim_fseek(myDisk->file, sectorFileOffset-1, SEEK_SET); sectRecordType = (uint8)fgetc(myDisk->file); switch(sectRecordType) { case SECT_RECORD_UNAVAILABLE: /* Data could not be read from the original media */ *flags |= IMD_DISK_IO_ERROR_GENERAL; break; case SECT_RECORD_NORM_ERR: /* Normal Data with read error */ case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ *flags |= IMD_DISK_IO_ERROR_CRC; /* fall through */ case SECT_RECORD_NORM: /* Normal Data */ case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ /* sim_debug(myDisk->debugmask, myDisk->device, "Uncompressed Data\n"); */ if (sim_fread(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file) != myDisk->track[Cyl][Head].sectsize) { sim_printf("SIM_IMD[%s]: sim_fread error for SECT_RECORD_NORM_DAM.\n", __FUNCTION__); } *readlen = myDisk->track[Cyl][Head].sectsize; break; case SECT_RECORD_NORM_COMP_ERR: /* Compressed Normal Data */ case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ *flags |= IMD_DISK_IO_ERROR_CRC; /* fall through */ case SECT_RECORD_NORM_COMP: /* Compressed Normal Data */ case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ /* sim_debug(myDisk->debugmask, myDisk->device, "Compressed Data\n"); */ memset(buf, fgetc(myDisk->file), myDisk->track[Cyl][Head].sectsize); *readlen = myDisk->track[Cyl][Head].sectsize; *flags |= IMD_DISK_IO_COMPRESSED; break; default: sim_printf("ERROR: unrecognized sector record type %d\n", sectRecordType); break; } /* Set flags for deleted address mark. */ switch(sectRecordType) { case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ *flags |= IMD_DISK_IO_DELETED_ADDR_MARK; default: break; } return(SCPE_OK); } /* Write a sector to an IMD image. */ t_stat sectWrite(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 Sector, uint8 *buf, uint32 buflen, uint32 *flags, uint32 *writelen) { uint32 sectorFileOffset; uint8 sectRecordType; uint8 start_sect; *writelen = 0; sim_debug(myDisk->debugmask, myDisk->device, "Writing C:%d/H:%d/S:%d, len=%d\n", Cyl, Head, Sector, buflen); /* Check parameters */ if(myDisk == NULL) { *flags = IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(sectSeek(myDisk, Cyl, Head) != 0) { *flags = IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(Sector > myDisk->track[Cyl][Head].nsects) { sim_debug(myDisk->debugmask, myDisk->device, "%s: invalid sector\n", __FUNCTION__); *flags = IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(myDisk->flags & FD_FLAG_WRITELOCK) { sim_printf("Disk write-protected because the image contains compressed sectors. Use IMDU to uncompress.\n"); *flags = IMD_DISK_IO_ERROR_WPROT; return(SCPE_IOERR); } if(buflen < myDisk->track[Cyl][Head].sectsize) { sim_printf("%s: user buffer too short [buflen %i < sectsize %i]\n", __FUNCTION__, buflen, myDisk->track[Cyl][Head].sectsize); *flags = IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } start_sect = myDisk->track[Cyl][Head].start_sector; sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect]; (void)sim_fseek(myDisk->file, sectorFileOffset-1, SEEK_SET); if (*flags & IMD_DISK_IO_ERROR_GENERAL) { sectRecordType = SECT_RECORD_UNAVAILABLE; } else if (*flags & IMD_DISK_IO_ERROR_CRC) { if (*flags & IMD_DISK_IO_DELETED_ADDR_MARK) sectRecordType = SECT_RECORD_NORM_DAM_ERR; else sectRecordType = SECT_RECORD_NORM_ERR; } else { if (*flags & IMD_DISK_IO_DELETED_ADDR_MARK) sectRecordType = SECT_RECORD_NORM_DAM; else sectRecordType = SECT_RECORD_NORM; } fputc(sectRecordType, myDisk->file); sim_fwrite(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file); *writelen = myDisk->track[Cyl][Head].sectsize; return(SCPE_OK); } /* Format an entire track. The new track to be formatted must be after any existing tracks on * the disk. * * This routine should be enhanced to re-format an existing track to the same format (this * does not involve changing the disk image size.) * * Any existing data on the disk image will be destroyed when Track 0, Head 0 is formatted. * At that time, the IMD file is truncated. So for the trackWrite to be used to successfully * format a disk image, then format program must format tracks starting with Cyl 0, Head 0, * and proceed sequentially through all tracks/heads on the disk. * * Format programs that are known to work include: * Cromemco CDOS "INIT.COM" * ADC Super-Six (CP/M-80) "FMT8.COM" * 86-DOS "INIT.COM" * MS-DOS 1.25 "FORMAT.COM" - SSSD 8" * MS-DOS 1.25 "FORMAT.COM /D" - DSDD 8" * OASIS-80 v5.6 "INITDISK A (FORMAT,SECTOR 13)" - SSSD 8" * OASIS-80 v5.6 "INITDISK A (FORMAT)" - SSDD 8" * OASIS-80 v5.6 "INITDISK A (FORMAT,HEAD 2)" - DSDD 8" */ t_stat trackWrite(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 numSectors, uint32 sectorLen, uint8 *sectorMap, uint8 mode, uint8 fillbyte, uint32 *flags) { FILE *fileref; IMD_HEADER track_header = { 0 }; uint8 *sectorData; t_addr comment; unsigned long i; unsigned long dataLen; uint8 sectsize = 0; *flags = 0; /* Check parameters */ if(myDisk == NULL) { *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(myDisk->flags & FD_FLAG_WRITELOCK) { sim_printf("Disk write-protected, cannot format tracks.\n"); *flags |= IMD_DISK_IO_ERROR_WPROT; return(SCPE_IOERR); } fileref = myDisk->file; sim_debug(myDisk->debugmask, myDisk->device, "Formatting C:%d/H:%d/N:%d, len=%d, Fill=0x%02x\n", Cyl, Head, numSectors, sectorLen, fillbyte); /* Truncate the IMD file when formatting Cyl 0, Head 0 */ if((Cyl == 0) && (Head == 0)) { /* Skip over IMD comment field. */ commentParse(myDisk, NULL, 0); /* Truncate the IMD file after the comment field. */ if (((comment = (t_addr)ftell (fileref)) == (t_addr)-1) || (sim_set_fsize(fileref, comment) == -1)) { sim_printf("Disk truncation failed.\n"); *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } /* Flush and re-parse the IMD file. */ fflush(fileref); diskParse(myDisk, 0); } /* Check to make sure the Cyl / Head is not already formatted. */ if(sectSeek(myDisk, Cyl, Head) == 0) { sim_printf("SIM_IMD: ERROR: Not Formatting C:%d/H:%d, track already exists.\n", Cyl, Head); *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } track_header.mode = mode; track_header.cyl = Cyl; track_header.head = Head; track_header.nsects = numSectors; for (i = (sectorLen >> 8); i; i >>= 1) { sectsize++; } if (sectsize > IMD_MAX_SECTSIZE) { sim_printf("SIM_IMD: ERROR: Invalid sectsize %d\n", sectsize); return(SCPE_IERR); } track_header.sectsize = sectsize; /* Forward to end of the file, write track header and sector map. */ (void)sim_fseek(myDisk->file, 0, SEEK_END); sim_fwrite(&track_header, 1, sizeof(IMD_HEADER), fileref); sim_fwrite(sectorMap, 1, numSectors, fileref); /* Compute data length, and fill a sector buffer with the * sector record type as the first byte, and fill the sector * data with the fillbyte. */ dataLen = sectorLen + 1; sectorData = (uint8 *)malloc(dataLen); if (sectorData == NULL) { sim_printf("%s: %s(): memory allocation failure.\n", __FILE__, __FUNCTION__); return SCPE_MEM; } memset(sectorData, fillbyte, dataLen); sectorData[0] = SECT_RECORD_NORM; /* For each sector on the track, write the record type and sector data. */ for(i=0;ifileref); rewind(uptr->fileref); if (fgets(header, 4, uptr->fileref) == NULL) uptr->u3 = IMAGE_TYPE_DSK; else if (strncmp(header, "IMD", 3) == 0) uptr->u3 = IMAGE_TYPE_IMD; else if(strncmp(header, "CPT", 3) == 0) { sim_printf("CPT images not yet supported.\n"); uptr->u3 = IMAGE_TYPE_CPT; result = SCPE_OPENERR; } else uptr->u3 = IMAGE_TYPE_DSK; (void)sim_fseeko(uptr->fileref, pos, SEEK_SET); return result; }