root/src/simh/sim_disk.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sim_disk_set_fmt
  2. sim_disk_show_fmt
  3. sim_disk_set_capac
  4. sim_disk_show_capac
  5. sim_disk_isavailable
  6. sim_disk_isavailable_a
  7. sim_disk_wrp
  8. sim_disk_size
  9. _sim_disk_rdsect
  10. sim_disk_rdsect
  11. sim_disk_rdsect_a
  12. _sim_disk_wrsect
  13. sim_disk_wrsect
  14. sim_disk_wrsect_a
  15. sim_disk_unload
  16. _sim_disk_io_flush
  17. _err_return
  18. ODS2Checksum
  19. get_filesystem_size
  20. sim_disk_attach
  21. sim_disk_detach
  22. sim_disk_attach_help
  23. sim_disk_reset
  24. sim_disk_perror
  25. sim_disk_clearerr
  26. sim_disk_data_trace

   1 /*
   2  * sim_disk.c: simulator disk support library
   3  *
   4  * vim: filetype=c:tabstop=4:ai:expandtab
   5  * SPDX-License-Identifier: MIT
   6  * scspell-id: b2c7f6c3-f62a-11ec-9f60-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 2011 Mark Pizzolato
  11  * Copyright (c) 2021-2025 The DPS8M Development Team
  12  *
  13  * Permission is hereby granted, free of charge, to any person obtaining a
  14  * copy of this software and associated documentation files (the "Software"),
  15  * to deal in the Software without restriction, including without limitation
  16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17  * and/or sell copies of the Software, and to permit persons to whom the
  18  * Software is furnished to do so, subject to the following conditions:
  19  *
  20  * The above copyright notice and this permission notice shall be included
  21  * in all copies or substantial portions of the Software.
  22  *
  23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  26  * IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR
  27  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  28  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  29  * OTHER DEALINGS IN THE SOFTWARE.
  30  *
  31  * Except as contained in this notice, the names of Mark Pizzolato shall not
  32  * be used in advertising or otherwise to promote the sale, use or other
  33  * dealings in this Software without prior written authorization from Mark
  34  * Pizzolato.
  35  *
  36  * ---------------------------------------------------------------------------
  37  */
  38 
  39 /*
  40  * Public routines:
  41  *
  42  * sim_disk_attach           attach disk unit
  43  * sim_disk_detach           detach disk unit
  44  * sim_disk_attach_help      help routine for attaching disks
  45  * sim_disk_rdsect           read disk sectors
  46  * sim_disk_rdsect_a         read disk sectors asynchronously
  47  * sim_disk_wrsect           write disk sectors
  48  * sim_disk_wrsect_a         write disk sectors asynchronously
  49  * sim_disk_unload           unload or detach a disk as needed
  50  * sim_disk_reset            reset unit
  51  * sim_disk_wrp              TRUE if write protected
  52  * sim_disk_isavailable      TRUE if available for I/O
  53  * sim_disk_size             get disk size
  54  * sim_disk_set_fmt          set disk format
  55  * sim_disk_show_fmt         show disk format
  56  * sim_disk_set_capac        set disk capacity
  57  * sim_disk_show_capac       show disk capacity
  58  * sim_disk_data_trace       debug support
  59  */
  60 
  61 #include "sim_defs.h"
  62 #include "sim_disk.h"
  63 
  64 #include <ctype.h>
  65 #include <signal.h>
  66 #include <sys/stat.h>
  67 
  68 #include "../decNumber/decContext.h"
  69 #include "../decNumber/decNumberLocal.h"
  70 
  71 #if defined(_WIN32)
  72 # include <windows.h>
  73 #endif /* if defined(_WIN32) */
  74 
  75 #if !defined(DECLITEND)
  76 # error Unknown platform endianness
  77 #endif /* if !defined(DECLITEND) */
  78 
  79 #if defined(NO_LOCALE)
  80 # define xstrerror_l strerror
  81 #endif
  82 
  83 #if defined(FREE)
  84 # undef FREE
  85 #endif /* if defined(FREE) */
  86 #define FREE(p) do  \
  87   {                 \
  88     free((p));      \
  89     (p) = NULL;     \
  90   } while(0)
  91 
  92 struct disk_context {
  93     DEVICE              *dptr;              /* Device for unit (access to debug flags) */
  94     uint32              dbit;               /* debugging bit */
  95     uint32              sector_size;        /* Disk Sector Size (of the pseudo disk) */
  96     uint32              capac_factor;       /* Units of Capacity (2 = word, 1 = byte) */
  97     uint32              xfer_element_size;  /* Disk Bus Transfer size (1 - byte, 2 - word, 4 - longword) */
  98     uint32              storage_sector_size;/* Sector size of the containing storage */
  99     uint32              removable;          /* Removable device flag */
 100     uint32              auto_format;        /* Format determined dynamically */
 101     };
 102 
 103 #define disk_ctx up8                        /* Field in Unit structure which points to the disk_context */
 104 
 105 /* Forward declarations */
 106 
 107 struct sim_disk_fmt {
 108     const char          *name;                          /* name */
 109     int32               uflags;                         /* unit flags */
 110     int32               fmtval;                         /* Format type value */
 111     t_stat              (*impl_fnc)(void);              /* Implemented Test Function */
 112     };
 113 
 114 static struct sim_disk_fmt fmts[DKUF_N_FMT] = {
 115     { "SIMH", 0, DKUF_F_STD, NULL},
 116     { NULL,   0, 0,          0   } };
 117 
 118 /* Set disk format */
 119 
 120 t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122 uint32 f;
 123 
 124 if (uptr == NULL)
 125     return SCPE_IERR;
 126 if (cptr == NULL)
 127     return SCPE_ARG;
 128 for (f = 0; f < DKUF_N_FMT && fmts[f].name; f++) {
 129     if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) {
 130         if ((fmts[f].impl_fnc) && (fmts[f].impl_fnc() != SCPE_OK))
 131             return SCPE_NOFNC;
 132         uptr->flags = (uptr->flags & ~DKUF_FMT) |
 133             (fmts[f].fmtval << DKUF_V_FMT) | fmts[f].uflags;
 134         return SCPE_OK;
 135         }
 136     }
 137 return SCPE_ARG;
 138 }
 139 
 140 /* Show disk format */
 141 
 142 t_stat sim_disk_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144 int32 f = DK_GET_FMT (uptr);
 145 size_t i;
 146 
 147 for (i = 0; i < DKUF_N_FMT; i++)
 148     if (fmts[i].fmtval == f) {
 149         fprintf (st, "%s format", fmts[i].name);
 150         return SCPE_OK;
 151         }
 152 fprintf (st, "invalid format");
 153 return SCPE_OK;
 154 }
 155 
 156 /* Set disk capacity */
 157 
 158 t_stat sim_disk_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 159 {
 160 t_offset cap;
 161 t_stat r;
 162 DEVICE *dptr = find_dev_from_unit (uptr);
 163 
 164 if ((cptr == NULL) || (*cptr == 0))
 165     return SCPE_ARG;
 166 if (uptr->flags & UNIT_ATT)
 167     return SCPE_ALATT;
 168 cap = (t_offset) get_uint (cptr, 10, 2000000, &r);
 169 if (r != SCPE_OK)
 170     return SCPE_ARG;
 171 uptr->capac = (t_addr)((cap * ((t_offset) 1000000))/((dptr->flags & DEV_SECTORS) ? 512 : 1));
 172 return SCPE_OK;
 173 }
 174 
 175 /* Show disk capacity */
 176 
 177 t_stat sim_disk_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179 const char *cap_units = "B";
 180 DEVICE *dptr = find_dev_from_unit (uptr);
 181 t_offset capac = ((t_offset)uptr->capac)*((dptr->flags & DEV_SECTORS) ? 512 : 1);
 182 
 183 if ((dptr->dwidth / dptr->aincr) == 16)
 184     cap_units = "W";
 185 if (capac) {
 186     if (capac >= (t_offset) 1000000)
 187         fprintf (st, "capacity=%luM%s", (unsigned long) (capac / ((t_offset) 1000000)), cap_units);
 188     else if (uptr->capac >= (t_addr) 1000)
 189         fprintf (st, "capacity=%luK%s", (unsigned long) (capac / ((t_offset) 1000)), cap_units);
 190     else fprintf (st, "capacity=%lu%s", (unsigned long) capac, cap_units);
 191     }
 192 else fprintf (st, "undefined capacity");
 193 return SCPE_OK;
 194 }
 195 
 196 /* Test for available */
 197 
 198 t_bool sim_disk_isavailable (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 199 {
 200 if (!(uptr->flags & UNIT_ATT))                          /* attached? */
 201     return FALSE;
 202 switch (DK_GET_FMT (uptr)) {                            /* case on format */
 203     case DKUF_F_STD:                                    /* SIMH format */
 204         return TRUE;
 205         /*NOTREACHED*/ /* unreachable */
 206         break;
 207     default:
 208         return FALSE;
 209     }
 210 }
 211 
 212 t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
 213 {
 214 t_bool r = FALSE;
 215     r = sim_disk_isavailable (uptr);
 216 return r;
 217 }
 218 
 219 /* Test for write protect */
 220 
 221 t_bool sim_disk_wrp (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 222 {
 223 return (uptr->flags & DKUF_WRP)? TRUE: FALSE;
 224 }
 225 
 226 /* Get Disk size */
 227 
 228 t_offset sim_disk_size (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230 switch (DK_GET_FMT (uptr)) {                            /* case on format */
 231     case DKUF_F_STD:                                    /* SIMH format */
 232         return sim_fsize_ex (uptr->fileref);
 233         /*NOTREACHED*/ /* unreachable */
 234         break;
 235     default:
 236         return (t_offset)-1;
 237     }
 238 }
 239 
 240 /* Read Sectors */
 241 
 242 static t_stat _sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
     /* [previous][next][first][last][top][bottom][index][help] */
 243 {
 244 t_offset da;
 245 uint32 err, tbc;
 246 size_t i;
 247 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
 248 
 249 sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_rdsect(unit=%lu, lba=0x%X, sects=%lu)\n",
 250            (unsigned long)(uptr-ctx->dptr->units), lba, (unsigned long)sects);
 251 
 252 da = ((t_offset)lba) * ctx->sector_size;
 253 tbc = sects * ctx->sector_size;
 254 if (sectsread)
 255     *sectsread = 0;
 256 err = sim_fseeko (uptr->fileref, da, SEEK_SET);          /* set pos */
 257 if (!err) {
 258     i = sim_fread (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref);
 259     if (i < tbc/ctx->xfer_element_size)                 /* fill */
 260         (void)memset (&buf[i*ctx->xfer_element_size], 0, tbc-(i*ctx->xfer_element_size));
 261     err = ferror (uptr->fileref);
 262     if ((!err) && (sectsread))
 263         *sectsread = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size);
 264     }
 265 return err;
 266 }
 267 
 268 t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
     /* [previous][next][first][last][top][bottom][index][help] */
 269 {
 270 t_stat r;
 271 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
 272 t_seccnt sread = 0;
 273 
 274 sim_debug (ctx->dbit, ctx->dptr, "sim_disk_rdsect(unit=%lu, lba=0x%X, sects=%lu)\n",
 275            (unsigned long)(uptr-ctx->dptr->units), lba, (unsigned long)sects);
 276 
 277 if ((sects == 1) &&                                    /* Single sector reads beyond the end of the disk */
 278     (lba >= (uptr->capac*ctx->capac_factor)/(ctx->sector_size/((ctx->dptr->flags & DEV_SECTORS) ? 512 : 1)))) {
 279     (void)memset (buf, '\0', ctx->sector_size);        /* are bad block management efforts - zero buffer */
 280     if (sectsread)
 281         *sectsread = 1;
 282     return SCPE_OK;                                     /* return success */
 283     }
 284 
 285 if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) ||   /* Sector Aligned & whole sector transfers */
 286     ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) &&
 287      (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) {
 288     switch (DK_GET_FMT (uptr)) {                        /* case on format */
 289         case DKUF_F_STD:                                /* SIMH format */
 290             return _sim_disk_rdsect (uptr, lba, buf, sectsread, sects);
 291             /*NOTREACHED*/ /* unreachable */
 292             break;
 293         default:
 294             return SCPE_NOFNC;
 295         }
 296 //    if (sectsread)
 297 //        *sectsread = sread;
 298 //    if (r != SCPE_OK)
 299 //        return r;
 300 //    sim_buf_swap_data (buf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size);
 301 //    return r;
 302     }
 303 else { /* Unaligned and/or partial sector transfers */
 304     uint8 *tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size);
 305     t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */
 306     t_lba tlba = lba & ~(sspsts - 1);
 307     t_seccnt tsects = sects + (lba - tlba);
 308 
 309     tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1);
 310     if (sectsread)
 311         *sectsread = 0;
 312     if (tbuf == NULL)
 313         return SCPE_MEM;
 314     switch (DK_GET_FMT (uptr)) {                        /* case on format */
 315         case DKUF_F_STD:                                /* SIMH format */
 316             r = _sim_disk_rdsect (uptr, tlba, tbuf, &sread, tsects);
 317             break;
 318         default:
 319             FREE (tbuf);
 320             return SCPE_NOFNC;
 321         }
 322     if (r == SCPE_OK) {
 323         memcpy (buf, tbuf + ((lba - tlba) * ctx->sector_size), sects * ctx->sector_size);
 324         if (sectsread) {
 325             *sectsread = sread - (lba - tlba);
 326             if (*sectsread > sects)
 327                 *sectsread = sects;
 328             }
 329         }
 330     FREE (tbuf);
 331     return r;
 332     }
 333 }
 334 
 335 t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
 336 {
 337 t_stat r = SCPE_OK;
 338     r = sim_disk_rdsect (uptr, lba, buf, sectsread, sects);
 339 return r;
 340 }
 341 
 342 /* Write Sectors */
 343 
 344 static t_stat _sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
     /* [previous][next][first][last][top][bottom][index][help] */
 345 {
 346 t_offset da;
 347 uint32 err, tbc;
 348 size_t i;
 349 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
 350 
 351 sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_wrsect(unit=%lu, lba=0x%X, sects=%lu)\n",
 352            (unsigned long)(uptr-ctx->dptr->units), lba, (unsigned long)sects);
 353 
 354 da = ((t_offset)lba) * ctx->sector_size;
 355 tbc = sects * ctx->sector_size;
 356 if (sectswritten)
 357     *sectswritten = 0;
 358 err = sim_fseeko (uptr->fileref, da, SEEK_SET);          /* set pos */
 359 if (!err) {
 360     i = sim_fwrite (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref);
 361     err = ferror (uptr->fileref);
 362     if ((!err) && (sectswritten))
 363         *sectswritten = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size);
 364     }
 365 return err;
 366 }
 367 
 368 t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
     /* [previous][next][first][last][top][bottom][index][help] */
 369 {
 370 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
 371 uint32 f = DK_GET_FMT (uptr);
 372 t_stat r;
 373 uint8 *tbuf = NULL;
 374 
 375 sim_debug (ctx->dbit, ctx->dptr, "sim_disk_wrsect(unit=%lu, lba=0x%X, sects=%lu)\n",
 376            (unsigned long)(uptr-ctx->dptr->units), lba, (unsigned long)sects);
 377 
 378 if (uptr->dynflags & UNIT_DISK_CHK) {
 379     DEVICE *dptr = find_dev_from_unit (uptr);
 380     uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */
 381     t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(ctx->sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1)));
 382     t_lba sect;
 383 
 384     for (sect = 0; sect < sects; sect++) {
 385         t_lba offset;
 386         t_bool sect_error = FALSE;
 387 
 388         for (offset = 0; offset < ctx->sector_size; offset += sizeof(uint32)) {
 389             if (*((uint32 *)&buf[sect*ctx->sector_size + offset]) != (uint32)(lba + sect)) {
 390                 sect_error = TRUE;
 391                 break;
 392                 }
 393             }
 394         if (sect_error) {
 395             uint32 save_dctrl = dptr->dctrl;
 396             FILE *save_sim_deb = sim_deb;
 397 
 398             sim_printf ("\n%s%lu: Write Address Verification Error on lbn %lu(0x%X) of %lu(0x%X).\n",
 399                         sim_dname (dptr), (unsigned long)(uptr-dptr->units),
 400                         (unsigned long)((unsigned long)lba+(unsigned long)sect),
 401                         (int)((int)lba+(int)sect), (unsigned long)total_sectors, (int)total_sectors);
 402             dptr->dctrl = 0xFFFFFFFF;
 403             sim_deb = save_sim_deb ? save_sim_deb : stdout;
 404             sim_disk_data_trace (uptr, buf+sect*ctx->sector_size, lba+sect, ctx->sector_size,    "Found", TRUE, 1);
 405             dptr->dctrl = save_dctrl;
 406             sim_deb = save_sim_deb;
 407             }
 408         }
 409     }
 410 if (f == DKUF_F_STD)
 411     return _sim_disk_wrsect (uptr, lba, buf, sectswritten, sects);
 412 if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) ||   /* Sector Aligned & whole sector transfers */
 413     ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) &&
 414      (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) {
 415         sim_end = DECLITEND;
 416         if (sim_end || (ctx->xfer_element_size == sizeof (char)))
 417             switch (DK_GET_FMT (uptr)) {                            /* case on format */
 418                 default:
 419                     return SCPE_NOFNC;
 420             }
 421 
 422         tbuf = (uint8*) malloc (sects * ctx->sector_size);
 423         if (NULL == tbuf)
 424             return SCPE_MEM;
 425         sim_buf_copy_swapped (tbuf, buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size);
 426 
 427         switch (DK_GET_FMT (uptr)) {                            /* case on format */
 428             default:
 429                 r = SCPE_NOFNC;
 430                 break;
 431             }
 432         }
 433 else { /* Unaligned and/or partial sector transfers */
 434     t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */
 435     t_lba tlba = lba & ~(sspsts - 1);
 436     t_seccnt tsects = sects + (lba - tlba);
 437 
 438     tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size);
 439     tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1);
 440     if (sectswritten)
 441         *sectswritten = 0;
 442     if (tbuf == NULL)
 443         return SCPE_MEM;
 444     /* Partial Sector writes require a read-modify-write sequence for the partial sectors */
 445     if ((lba & (sspsts - 1)) ||
 446         (sects < sspsts))
 447         switch (DK_GET_FMT (uptr)) {                            /* case on format */
 448             default:
 449                 r = SCPE_NOFNC;
 450                 break;
 451             }
 452     if ((tsects > sspsts) &&
 453         ((sects + lba - tlba) & (sspsts - 1)))
 454         switch (DK_GET_FMT (uptr)) {                            /* case on format */
 455             default:
 456                 r = SCPE_NOFNC;
 457                 break;
 458             }
 459     sim_buf_copy_swapped (tbuf + (lba & (sspsts - 1)) * ctx->sector_size,
 460                           buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size);
 461     switch (DK_GET_FMT (uptr)) {                            /* case on format */
 462         default:
 463             r = SCPE_NOFNC;
 464             break;
 465         }
 466     // if ((r == SCPE_OK) && sectswritten) { //-V560
 467     //     *sectswritten -= (lba - tlba);
 468     //     if (*sectswritten > sects)
 469     //         *sectswritten = sects;
 470     //     }
 471     }
 472 FREE (tbuf);
 473 return r;
 474 }
 475 
 476 t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback)
     /* [previous][next][first][last][top][bottom][index][help] */
 477 {
 478 t_stat r = SCPE_OK;
 479     r =  sim_disk_wrsect (uptr, lba, buf, sectswritten, sects);
 480 return r;
 481 }
 482 
 483 t_stat sim_disk_unload (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 484 {
 485 switch (DK_GET_FMT (uptr)) {                            /* case on format */
 486     case DKUF_F_STD:                                    /* Simh */
 487         return sim_disk_detach (uptr);
 488     default:
 489         return SCPE_NOFNC;
 490     }
 491 }
 492 
 493 static void _sim_disk_io_flush (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 494 {
 495 uint32 f = DK_GET_FMT (uptr);
 496 
 497 switch (f) {                                            /* case on format */
 498     case DKUF_F_STD:                                    /* Simh */
 499         (void)fflush (uptr->fileref);
 500         break;
 501         }
 502 }
 503 
 504 static t_stat _err_return (UNIT *uptr, t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
 505 {
 506 FREE (uptr->filename);
 507 uptr->filename = NULL;
 508 FREE (uptr->disk_ctx);
 509 uptr->disk_ctx = NULL;
 510 return stat;
 511 }
 512 
 513 #if defined(__xlc__)
 514 # pragma pack(1)
 515 #else
 516 # pragma pack(push,1)
 517 #endif
 518 typedef struct _ODS2_HomeBlock
 519     {
 520     uint32 hm2_l_homelbn;
 521     uint32 hm2_l_alhomelbn;
 522     uint32 hm2_l_altidxlbn;
 523     uint8  hm2_b_strucver;
 524     uint8  hm2_b_struclev;
 525     uint16 hm2_w_cluster;
 526     uint16 hm2_w_homevbn;
 527     uint16 hm2_w_alhomevbn;
 528     uint16 hm2_w_altidxvbn;
 529     uint16 hm2_w_ibmapvbn;
 530     uint32 hm2_l_ibmaplbn;
 531     uint32 hm2_l_maxfiles;
 532     uint16 hm2_w_ibmapsize;
 533     uint16 hm2_w_resfiles;
 534     uint16 hm2_w_devtype;
 535     uint16 hm2_w_rvn;
 536     uint16 hm2_w_setcount;
 537     uint16 hm2_w_volchar;
 538     uint32 hm2_l_volowner;
 539     uint32 hm2_l_reserved;
 540     uint16 hm2_w_protect;
 541     uint16 hm2_w_fileprot;
 542     uint16 hm2_w_reserved;
 543     uint16 hm2_w_checksum1;
 544     uint32 hm2_q_credate[2];
 545     uint8  hm2_b_window;
 546     uint8  hm2_b_lru_lim;
 547     uint16 hm2_w_extend;
 548     uint32 hm2_q_retainmin[2];
 549     uint32 hm2_q_retainmax[2];
 550     uint32 hm2_q_revdate[2];
 551     uint8  hm2_r_min_class[20];
 552     uint8  hm2_r_max_class[20];
 553     uint8  hm2_r_reserved[320];
 554     uint32 hm2_l_serialnum;
 555     uint8  hm2_t_strucname[12];
 556     uint8  hm2_t_volname[12];
 557     uint8  hm2_t_ownername[12];
 558     uint8  hm2_t_format[12];
 559     uint16 hm2_w_reserved2;
 560     uint16 hm2_w_checksum2;
 561     } ODS2_HomeBlock;
 562 
 563 typedef struct _ODS2_FileHeader
 564     {
 565     uint8  fh2_b_idoffset;
 566     uint8  fh2_b_mpoffset;
 567     uint8  fh2_b_acoffset;
 568     uint8  fh2_b_rsoffset;
 569     uint16 fh2_w_seg_num;
 570     uint16 fh2_w_structlev;
 571     uint16 fh2_w_fid[3];
 572     uint16 fh2_w_ext_fid[3];
 573     uint16 fh2_w_recattr[16];
 574     uint32 fh2_l_filechar;
 575     uint16 fh2_w_remaining[228];
 576     } ODS2_FileHeader;
 577 
 578 typedef union _ODS2_Retreval
 579     {
 580         struct
 581             {
 582             unsigned fm2___fill   : 14;       /* type specific data               */
 583             unsigned fm2_v_format : 2;        /* format type code                 */
 584             } fm2_r_word0_bits;
 585         struct
 586             {
 587             unsigned fm2_v_exact    : 1;      /* exact placement specified        */
 588             unsigned fm2_v_oncyl    : 1;      /* on cylinder allocation desired   */
 589             unsigned fm2___fill     : 10;
 590             unsigned fm2_v_lbn      : 1;      /* use LBN of next map pointer      */
 591             unsigned fm2_v_rvn      : 1;      /* place on specified RVN           */
 592             unsigned fm2_v_format0  : 2;
 593             } fm2_r_map_bits0;
 594         struct
 595             {
 596             unsigned fm2_b_count1   : 8;      /* low byte described below         */
 597             unsigned fm2_v_highlbn1 : 6;      /* high order LBN                   */
 598             unsigned fm2_v_format1  : 2;
 599             unsigned fm2_w_lowlbn1  : 16;     /* low order LBN                    */
 600             } fm2_r_map_bits1;
 601         struct
 602             {
 603             struct
 604                 {
 605                 unsigned fm2_v_count2   : 14; /* count field                      */
 606                 unsigned fm2_v_format2  : 2;
 607                 unsigned fm2_l_lowlbn2  : 16; /* low order LBN                    */
 608                 } fm2_r_map2_long0;
 609             uint16 fm2_l_highlbn2;            /* high order LBN                   */
 610             } fm2_r_map_bits2;
 611         struct
 612             {
 613             struct
 614                 {
 615                 unsigned fm2_v_highcount3 : 14; /* low order count field          */
 616                 unsigned fm2_v_format3  : 2;
 617                 unsigned fm2_w_lowcount3 : 16;  /* high order count field         */
 618                 } fm2_r_map3_long0;
 619             uint32 fm2_l_lbn3;
 620             } fm2_r_map_bits3;
 621     } ODS2_Retreval;
 622 
 623 typedef struct _ODS2_StorageControlBlock
 624     {
 625     uint8  scb_b_strucver;   /* 1 */
 626     uint8  scb_b_struclev;   /* 2 */
 627     uint16 scb_w_cluster;
 628     uint32 scb_l_volsize;
 629     uint32 scb_l_blksize;
 630     uint32 scb_l_sectors;
 631     uint32 scb_l_tracks;
 632     uint32 scb_l_cylinder;
 633     uint32 scb_l_status;
 634     uint32 scb_l_status2;
 635     uint16 scb_w_writecnt;
 636     uint8  scb_t_volockname[12];
 637     uint32 scb_q_mounttime[2];
 638     uint16 scb_w_backrev;
 639     uint32 scb_q_genernum[2];
 640     uint8  scb_b_reserved[446];
 641     uint16 scb_w_checksum;
 642     } ODS2_SCB;
 643 #if defined(__xlc__)
 644 # pragma pack(reset)
 645 #else
 646 # pragma pack(pop)
 647 #endif
 648 
 649 static uint16
 650 ODS2Checksum (void *Buffer, uint16 WordCount)
     /* [previous][next][first][last][top][bottom][index][help] */
 651     {
 652     int i;
 653     uint16 CheckSum = 0;
 654     uint16 *Buf = (uint16 *)Buffer;
 655 
 656     for (i=0; i<WordCount; i++)
 657         CheckSum += Buf[i];
 658     return CheckSum;
 659     }
 660 
 661 static t_offset get_filesystem_size (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 662 {
 663 DEVICE *dptr;
 664 t_addr saved_capac;
 665 t_offset temp_capac = 512 * (t_offset)0xFFFFFFFFu;  /* Make sure we can access the largest sector */
 666 uint32 capac_factor;
 667 ODS2_HomeBlock Home;
 668 ODS2_FileHeader Header;
 669 ODS2_Retreval *Retr;
 670 ODS2_SCB Scb;
 671 uint16 CheckSum1, CheckSum2;
 672 uint32 ScbLbn = 0;
 673 t_offset ret_val = (t_offset)-1;
 674 
 675 if ((dptr = find_dev_from_unit (uptr)) == NULL)
 676     return ret_val;
 677 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* save capacity units (word: 2, byte: 1) */
 678 saved_capac = uptr->capac;
 679 /* cppcheck-suppress signConversion */
 680 uptr->capac = (t_addr)(temp_capac/(capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
 681 if (sim_disk_rdsect (uptr, 1, (uint8 *)&Home, NULL, 1))
 682     goto Return_Cleanup;
 683 /* cppcheck-suppress comparePointers */
 684 CheckSum1 = ODS2Checksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum1)-((char *)&Home.hm2_l_homelbn))/2));
 685 /* cppcheck-suppress comparePointers */
 686 CheckSum2 = ODS2Checksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum2)-((char *)&Home.hm2_l_homelbn))/2));
 687 if ((Home.hm2_l_homelbn   == 0)  ||
 688     (Home.hm2_l_alhomelbn == 0)  ||
 689     (Home.hm2_l_altidxlbn == 0)  ||
 690    ((Home.hm2_b_struclev  != 2)  &&
 691     (Home.hm2_b_struclev  != 5)) ||
 692     (Home.hm2_b_strucver  == 0)  ||
 693     (Home.hm2_w_cluster   == 0)  ||
 694     (Home.hm2_w_homevbn   == 0)  ||
 695     (Home.hm2_w_alhomevbn == 0)  ||
 696     (Home.hm2_w_ibmapvbn  == 0)  ||
 697     (Home.hm2_l_ibmaplbn  == 0)  ||
 698     (Home.hm2_w_resfiles  >= Home.hm2_l_maxfiles) ||
 699     (Home.hm2_w_ibmapsize == 0)  ||
 700     (Home.hm2_w_resfiles   < 5)  ||
 701     (Home.hm2_w_checksum1 != CheckSum1) ||
 702     (Home.hm2_w_checksum2 != CheckSum2))
 703     goto Return_Cleanup;
 704 if (sim_disk_rdsect (uptr, Home.hm2_l_ibmaplbn+Home.hm2_w_ibmapsize+1, (uint8 *)&Header, NULL, 1))
 705     goto Return_Cleanup;
 706 CheckSum1 = ODS2Checksum (&Header, 255);
 707 if (CheckSum1 != *(((uint16 *)&Header)+255)) //-V1032 /* Verify Checksum on BITMAP.SYS file header */
 708     goto Return_Cleanup;
 709 Retr = (ODS2_Retreval *)(((uint16*)(&Header))+Header.fh2_b_mpoffset);
 710 /* The BitMap File has a single extent, which may be preceded by a placement descriptor */
 711 if (Retr->fm2_r_word0_bits.fm2_v_format == 0)
 712     Retr = (ODS2_Retreval *)(((uint16 *)Retr)+1); //-V1032 /* skip placement descriptor */
 713 switch (Retr->fm2_r_word0_bits.fm2_v_format)
 714     {
 715     case 1:
 716         ScbLbn = (Retr->fm2_r_map_bits1.fm2_v_highlbn1<<16)+Retr->fm2_r_map_bits1.fm2_w_lowlbn1;
 717         break;
 718     case 2:
 719         ScbLbn = (Retr->fm2_r_map_bits2.fm2_l_highlbn2<<16)+Retr->fm2_r_map_bits2.fm2_r_map2_long0.fm2_l_lowlbn2;
 720         break;
 721     case 3:
 722         ScbLbn = Retr->fm2_r_map_bits3.fm2_l_lbn3;
 723         break;
 724     }
 725 Retr = (ODS2_Retreval *)(((uint16 *)Retr)+Retr->fm2_r_word0_bits.fm2_v_format+1);
 726 if (sim_disk_rdsect (uptr, ScbLbn, (uint8 *)&Scb, NULL, 1))
 727     goto Return_Cleanup;
 728 CheckSum1 = ODS2Checksum (&Scb, 255);
 729 if (CheckSum1 != *(((uint16 *)&Scb)+255)) //-V1032 /* Verify Checksum on Storage Control Block */
 730     goto Return_Cleanup;
 731 if ((Scb.scb_w_cluster != Home.hm2_w_cluster) ||
 732     (Scb.scb_b_strucver != Home.hm2_b_strucver) ||
 733     (Scb.scb_b_struclev != Home.hm2_b_struclev))
 734     goto Return_Cleanup;
 735 if (!sim_quiet) {
 736     sim_printf ("%s%lu: '%s' Contains ODS%lu File system:\n",
 737                 sim_dname (dptr), (unsigned long)(uptr-dptr->units), uptr->filename, (unsigned long)Home.hm2_b_struclev);
 738     sim_printf ("%s%lu: Volume Name: %12.12s ",
 739                 sim_dname (dptr), (unsigned long)(uptr-dptr->units), Home.hm2_t_volname);
 740     sim_printf ("Format: %12.12s ",
 741                 Home.hm2_t_format);
 742     sim_printf ("SectorsInVolume: %lu\n",
 743                 (unsigned long)Scb.scb_l_volsize);
 744     }
 745 ret_val = ((t_offset)Scb.scb_l_volsize) * 512;
 746 
 747 Return_Cleanup:
 748 uptr->capac = saved_capac;
 749 return ret_val;
 750 }
 751 
 752 t_stat sim_disk_attach (UNIT *uptr, const char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize,
     /* [previous][next][first][last][top][bottom][index][help] */
 753                         uint32 dbit, const char *dtype, uint32 pdp11tracksize, int completion_delay)
 754 {
 755 struct disk_context *ctx;
 756 DEVICE *dptr;
 757 FILE *(*open_function)(const char *filename, const char *mode) = sim_fopen;
 758 FILE *(*create_function)(const char *filename, t_offset desiredsize) = NULL;
 759 t_offset (*size_function)(FILE *file) = NULL;
 760 t_stat (*storage_function)(FILE *file, uint32 *sector_size, uint32 *removable) = NULL;
 761 t_bool created = FALSE, copied = FALSE;
 762 t_bool auto_format = FALSE;
 763 t_offset capac, filesystem_capac;
 764 
 765 if (uptr->flags & UNIT_DIS)                             /* disabled? */
 766     return SCPE_UDIS;
 767 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
 768     return SCPE_NOATT;
 769 if ((dptr = find_dev_from_unit (uptr)) == NULL)
 770     return SCPE_NOATT;
 771 if (sim_switches & SWMASK ('F')) {                      /* format spec? */
 772     char gbuf[CBUFSIZE];
 773     cptr = get_glyph (cptr, gbuf, 0);                   /* get spec */
 774     if (*cptr == 0)                                     /* must be more */
 775         return SCPE_2FARG;
 776     if (sim_disk_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK)
 777         return sim_messagef (SCPE_ARG, "Invalid Disk Format: %s\n", gbuf);
 778     sim_switches = sim_switches & ~(SWMASK ('F'));      /* Record Format specifier already processed */
 779     auto_format = TRUE;
 780     }
 781 open_function = sim_fopen;
 782 size_function = sim_fsize_ex;
 783 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char));/* alloc name buf */
 784 uptr->disk_ctx = ctx = (struct disk_context *)calloc(1, sizeof(struct disk_context));
 785 if (!ctx)
 786 {
 787   fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 788           __func__, __FILE__, __LINE__);
 789 #if defined(USE_BACKTRACE)
 790 # if defined(SIGUSR2)
 791   (void)raise(SIGUSR2);
 792   /*NOTREACHED*/ /* unreachable */
 793 # endif /* if defined(SIGUSR2) */
 794 #endif /* if defined(USE_BACKTRACE) */
 795   abort();
 796 }
 797 if ((uptr->filename == NULL) || (uptr->disk_ctx == NULL))
 798     return _err_return (uptr, SCPE_MEM);
 799 #if defined(__GNUC__)
 800 # if !defined(__clang_version__)
 801 #  if __GNUC__ > 7
 802 #   if !defined(__INTEL_COMPILER)
 803 #    pragma GCC diagnostic push
 804 #    pragma GCC diagnostic ignored "-Wstringop-truncation"
 805 #   endif /* if !defined(__INTEL_COMPILER) */
 806 #  endif /* if __GNUC__ > 7 */
 807 # endif /* if !defined(__clang_version__) */
 808 #endif /* if defined(__GNUC__) */
 809 strncpy (uptr->filename, cptr, CBUFSIZE);               /* save name */
 810 #if defined(__GNUC__)
 811 # if !defined(__clang_version__)
 812 #  if __GNUC__ > 7
 813 #   if !defined(__INTEL_COMPILER)
 814 #    pragma GCC diagnostic pop
 815 #   endif /* if !defined(__INTEL_COMPILER) */
 816 #  endif /* if __GNUC__ > 7 */
 817 # endif /* if !defined(__clang_version__) */
 818 #endif /* if defined(__GNUC__) */
 819 ctx->sector_size = (uint32)sector_size;                 /* save sector_size */
 820 ctx->capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* save capacity units (word: 2, byte: 1) */
 821 ctx->xfer_element_size = (uint32)xfer_element_size;     /* save xfer_element_size */
 822 ctx->dptr = dptr;                                       /* save DEVICE pointer */
 823 ctx->dbit = dbit;                                       /* save debug bit */
 824 sim_debug (ctx->dbit, ctx->dptr, "sim_disk_attach(unit=%lu,filename='%s')\n",
 825            (unsigned long)(uptr-ctx->dptr->units), uptr->filename);
 826 ctx->auto_format = auto_format;                         /* save that we auto selected format */
 827 ctx->storage_sector_size = (uint32)sector_size;         /* Default */
 828 if ((sim_switches & SWMASK ('R')) ||                    /* read only? */
 829     ((uptr->flags & UNIT_RO) != 0)) {
 830     if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
 831         ((uptr->flags & UNIT_RO) == 0))
 832         return _err_return (uptr, SCPE_NORO);           /* no, error */
 833     uptr->fileref = open_function (cptr, "rb");         /* open rd only */
 834     if (uptr->fileref == NULL)                          /* open fail? */
 835         return _err_return (uptr, SCPE_OPENERR);        /* yes, error */
 836     uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
 837     if (!sim_quiet) {
 838         sim_printf ("%s%lu: unit is read only (%s)\n", sim_dname (dptr),
 839                     (unsigned long)(uptr-dptr->units), cptr);
 840         }
 841     }
 842 else {                                                  /* normal */
 843     uptr->fileref = open_function (cptr, "rb+");        /* open r/w */
 844     if (uptr->fileref == NULL) {                        /* open fail? */
 845         if ((errno == EROFS) || (errno == EACCES)) {    /* read only? */
 846             if ((uptr->flags & UNIT_ROABLE) == 0)       /* allowed? */
 847                 return _err_return (uptr, SCPE_NORO);   /* no error */
 848             uptr->fileref = open_function (cptr, "rb"); /* open rd only */
 849             if (uptr->fileref == NULL)                  /* open fail? */
 850                 return _err_return (uptr, SCPE_OPENERR);/* yes, error */
 851             uptr->flags = uptr->flags | UNIT_RO;        /* set rd only */
 852             if (!sim_quiet)
 853                 sim_printf ("%s%lu: unit is read only (%s)\n", sim_dname (dptr),
 854                             (unsigned long)(uptr-dptr->units), cptr);
 855             }
 856         else {                                          /* doesn't exist */
 857             if (sim_switches & SWMASK ('E'))            /* must exist? */
 858                 return _err_return (uptr, SCPE_OPENERR);/* yes, error */
 859             if (create_function) //-V547 /* cppcheck-suppress internalAstError */
 860                 uptr->fileref = create_function (cptr,  /* create new file */
 861                                                  ((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1));
 862             else
 863                 uptr->fileref = open_function (cptr, "wb+");/* open new file */
 864             if (uptr->fileref == NULL)                  /* open fail? */
 865                 return _err_return (uptr, SCPE_OPENERR);/* yes, error */
 866             if (!sim_quiet)
 867                 sim_printf ("%s%lu: creating new file (%s)\n", sim_dname (dptr),
 868                             (unsigned long)(uptr-dptr->units), cptr);
 869             created = TRUE;
 870             }
 871         }                                               /* end if null */
 872     }                                                   /* end else */
 873 uptr->flags = uptr->flags | UNIT_ATT;
 874 uptr->pos = 0;
 875 
 876 /* Get Device attributes if they are available */
 877 if (storage_function) //-V547
 878     storage_function (uptr->fileref, &ctx->storage_sector_size, &ctx->removable);
 879 
 880 if ((created) && (!copied)) { //-V560
 881     t_stat r = SCPE_OK;
 882     uint8 *secbuf = (uint8 *)calloc (1, ctx->sector_size);       /* alloc temp sector buf */
 883     if (secbuf == NULL)
 884         r = SCPE_MEM;
 885     if (r == SCPE_OK)
 886         r = sim_disk_wrsect (uptr,
 887                              (t_lba)(((((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)) - \
 888                               ctx->sector_size)/ctx->sector_size), secbuf, NULL, 1); /* Write Last Sector */
 889     if (r == SCPE_OK)
 890         r = sim_disk_wrsect (uptr, (t_lba)(0), secbuf, NULL, 1); /* Write First Sector */
 891     FREE (secbuf);
 892     if (r != SCPE_OK) {
 893         sim_disk_detach (uptr);                         /* report error now */
 894         remove (cptr);                                  /* remove the create file */
 895         return SCPE_OPENERR;
 896         }
 897     if (sim_switches & SWMASK ('I')) {                  /* Initialize To Sector Address */
 898         uint8 *init_buf = (uint8*) malloc (1024*1024);
 899         t_lba lba, sect;
 900         uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */
 901         t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size);
 902         t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1)));
 903         t_seccnt sects = sectors_per_buffer;
 904 
 905         if (!init_buf) {
 906             sim_disk_detach (uptr);                         /* report error now */
 907             remove (cptr);
 908             return SCPE_MEM;
 909             }
 910         for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) {
 911             sects = sectors_per_buffer;
 912             if (lba + sects > total_sectors)
 913                 sects = total_sectors - lba;
 914             for (sect = 0; sect < sects; sect++) {
 915                 t_lba offset;
 916                 for (offset = 0; offset < sector_size; offset += sizeof(uint32))
 917                     *((uint32 *)&init_buf[sect*sector_size + offset]) = (uint32)(lba + sect);
 918                 }
 919             r = sim_disk_wrsect (uptr, lba, init_buf, NULL, sects);
 920             if (r != SCPE_OK) {
 921                 FREE (init_buf);
 922                 sim_disk_detach (uptr);                         /* report error now */
 923                 remove (cptr);                                  /* remove the create file */
 924                 return SCPE_OPENERR;
 925                 }
 926             if (!sim_quiet)
 927                 sim_printf ("%s%lu: Initialized To Sector Address %luMB.  %lu%% complete.\r",
 928                             sim_dname (dptr), (unsigned long)(uptr-dptr->units),
 929                             (unsigned long)((((float)lba)*sector_size)/1000000),
 930                             (unsigned long)((((float)lba)*100)/total_sectors));
 931             }
 932         if (!sim_quiet)
 933             sim_printf ("%s%lu: Initialized To Sector Address %luMB.  100%% complete.\n",
 934                         sim_dname (dptr), (unsigned long)(uptr-dptr->units),
 935                         (unsigned long)((((float)lba)*sector_size)/1000000));
 936         FREE (init_buf);
 937         }
 938     }
 939 
 940 if (sim_switches & SWMASK ('K')) {
 941     t_stat r = SCPE_OK;
 942     t_lba lba, sect;
 943     uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */
 944     t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size);
 945     t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1)));
 946     t_seccnt sects = sectors_per_buffer;
 947     uint8 *verify_buf = (uint8*) malloc (1024*1024);
 948 
 949     if (!verify_buf) {
 950         sim_disk_detach (uptr);                         /* report error now */
 951         return SCPE_MEM;
 952         }
 953     for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) {
 954         sects = sectors_per_buffer;
 955         if (lba + sects > total_sectors)
 956             sects = total_sectors - lba;
 957         r = sim_disk_rdsect (uptr, lba, verify_buf, NULL, sects);
 958         if (r == SCPE_OK) {
 959             for (sect = 0; sect < sects; sect++) {
 960                 t_lba offset;
 961                 t_bool sect_error = FALSE;
 962 
 963                 for (offset = 0; offset < sector_size; offset += sizeof(uint32)) {
 964                     if (*((uint32 *)&verify_buf[sect*sector_size + offset]) != (uint32)(lba + sect)) {
 965                         sect_error = TRUE;
 966                         break;
 967                         }
 968                     }
 969                 if (sect_error) {
 970                     uint32 save_dctrl = dptr->dctrl;
 971                     FILE *save_sim_deb = sim_deb;
 972 
 973                     sim_printf ("\n%s%lu: Verification Error on lbn %lu(0x%X) of %lu(0x%X).\n", sim_dname (dptr),
 974                                 (unsigned long)(uptr-dptr->units),
 975                                 (unsigned long)((unsigned long)lba+(unsigned long)sect),
 976                                 (int)((int)lba+(int)sect),
 977                                 (unsigned long)total_sectors,
 978                                 (int)total_sectors);
 979                     dptr->dctrl = 0xFFFFFFFF;
 980                     sim_deb = stdout;
 981                     sim_disk_data_trace (uptr, verify_buf+sect*sector_size, lba+sect, sector_size,
 982                                          "Found", TRUE, 1);
 983                     dptr->dctrl = save_dctrl;
 984                     sim_deb = save_sim_deb;
 985                     }
 986                 }
 987             }
 988         if (!sim_quiet)
 989             sim_printf ("%s%lu: Verified containing Sector Address %luMB.  %lu%% complete.\r",
 990                         sim_dname (dptr), (unsigned long)(uptr-dptr->units),
 991                         (unsigned long)((((float)lba)*sector_size)/1000000),
 992                         (unsigned long)((((float)lba)*100)/total_sectors));
 993         }
 994     if (!sim_quiet)
 995         sim_printf ("%s%lu: Verified containing Sector Address %luMB.  100%% complete.\n",
 996                     sim_dname (dptr), (unsigned long)(uptr-dptr->units),
 997                     (unsigned long)((((float)lba)*sector_size)/1000000));
 998     FREE (verify_buf);
 999     uptr->dynflags |= UNIT_DISK_CHK;
1000     }
1001 
1002 filesystem_capac = get_filesystem_size (uptr);
1003 capac = size_function (uptr->fileref);
1004 if (capac && (capac != (t_offset)-1)) {
1005     if (dontautosize) {
1006         t_addr saved_capac = uptr->capac;
1007 
1008         if ((filesystem_capac != (t_offset)-1) &&
1009             (filesystem_capac > (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)))) {
1010             if (!sim_quiet) {
1011                 uptr->capac = (t_addr)(filesystem_capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
1012                 sim_printf ("%s%lu: The file system on the disk %s is larger than simulated device (%s > ",
1013                             sim_dname (dptr), (unsigned long)(uptr-dptr->units), cptr, sprint_capac (dptr, uptr));
1014                 uptr->capac = saved_capac;
1015                 sim_printf ("%s)\n", sprint_capac (dptr, uptr));
1016                 }
1017             sim_disk_detach (uptr);
1018             return SCPE_OPENERR;
1019             }
1020         if ((capac < (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) && \
1021             (DKUF_F_STD != DK_GET_FMT (uptr))) {
1022             if (!sim_quiet) {
1023                 uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
1024                 sim_printf ("%s%lu: non expandable disk %s is smaller than simulated device (%s < ",
1025                             sim_dname (dptr), (unsigned long)(uptr-dptr->units), cptr, sprint_capac (dptr, uptr));
1026                 uptr->capac = saved_capac;
1027                 sim_printf ("%s)\n", sprint_capac (dptr, uptr));
1028                 }
1029             sim_disk_detach (uptr);
1030             return SCPE_OPENERR;
1031             }
1032         }
1033     else {
1034         if ((filesystem_capac != (t_offset)-1) &&
1035             (filesystem_capac > capac))
1036             capac = filesystem_capac;
1037         if ((capac > (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) ||
1038             (DKUF_F_STD != DK_GET_FMT (uptr)))
1039             uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
1040         }
1041     }
1042 
1043 uptr->io_flush = _sim_disk_io_flush;
1044 
1045 return SCPE_OK;
1046 }
1047 
1048 t_stat sim_disk_detach (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1049 {
1050 struct disk_context *ctx;
1051 int (*close_function)(FILE *f);
1052 FILE *fileref;
1053 
1054 if ((uptr == NULL) || !(uptr->flags & UNIT_ATT))
1055     return SCPE_NOTATT;
1056 
1057 ctx = (struct disk_context *)uptr->disk_ctx;
1058 fileref = uptr->fileref;
1059 
1060 //sim_debug (ctx->dbit, ctx->dptr, "sim_disk_detach(unit=%lu,filename='%s')\n",
1061 //           (unsigned long)(uptr-ctx->dptr->units), uptr->filename);
1062 
1063 switch (DK_GET_FMT (uptr)) {                            /* case on format */
1064     case DKUF_F_STD:                                    /* Simh */
1065         close_function = fclose;
1066         break;
1067     default:
1068         return SCPE_IERR;
1069         }
1070 if (!(uptr->flags & UNIT_ATTABLE))                      /* attachable? */
1071     return SCPE_NOATT;
1072 if (!(uptr->flags & UNIT_ATT))                          /* attached? */
1073     return SCPE_OK;
1074 if (NULL == find_dev_from_unit (uptr))
1075     return SCPE_OK;
1076 
1077 if (uptr->io_flush)
1078     uptr->io_flush (uptr);                              /* flush buffered data */
1079 
1080 uptr->flags &= ~(UNIT_ATT | UNIT_RO);
1081 uptr->dynflags &= ~(UNIT_NO_FIO | UNIT_DISK_CHK);
1082 FREE (uptr->filename);
1083 uptr->filename = NULL;
1084 uptr->fileref = NULL;
1085 if (ctx && ctx -> auto_format)
1086     sim_disk_set_fmt (uptr, 0, "SIMH", NULL);           /* restore file format */
1087 if (uptr->disk_ctx) {
1088   FREE (uptr->disk_ctx);
1089   uptr->disk_ctx = NULL;
1090 }
1091 uptr->io_flush = NULL;
1092 if (close_function (fileref) == EOF)
1093     return SCPE_IOERR;
1094 return SCPE_OK;
1095 }
1096 
1097 t_stat sim_disk_attach_help(FILE *st, DEVICE *dptr, const UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1098 {
1099 fprintf (st, "%s Disk Attach Help\n\n", dptr->name);
1100 fprintf (st, "Disk files are stored in the following format:\n\n");
1101 fprintf (st, "    SIMH   An unstructured binary file of the size appropriate\n");
1102 fprintf (st, "           for the disk drive being simulated.\n\n");
1103 
1104 if (0 == (uptr-dptr->units)) {
1105     if (dptr->numunits > 1) {
1106         uint32 i;
1107 
1108         for (i=0; i < dptr->numunits; ++i)
1109             if (dptr->units[i].flags & UNIT_ATTABLE)
1110                 fprintf (st, "  sim> ATTACH {switches} %s%lu diskfile\n", dptr->name, (unsigned long)i);
1111         }
1112     else
1113         fprintf (st, "  sim> ATTACH {switches} %s diskfile\n", dptr->name);
1114     }
1115 else
1116     fprintf (st, "  sim> ATTACH {switches} %s diskfile\n\n", dptr->name);
1117 fprintf (st, "\n%s attach command switches\n", dptr->name);
1118 fprintf (st, "    -R          Attach Read Only.\n");
1119 fprintf (st, "    -E          Must Exist (if not specified an attempt to create the indicated\n");
1120 fprintf (st, "                disk container will be attempted).\n");
1121 fprintf (st, "    -F          Open the indicated disk container in a specific format\n");
1122 fprintf (st, "    -I          Initialize newly created disk so that each sector contains its\n");
1123 fprintf (st, "                sector address\n");
1124 fprintf (st, "    -K          Verify that the disk contents contain the sector address in each\n");
1125 fprintf (st, "                sector.  Whole disk checked at attach time and each sector is\n");
1126 fprintf (st, "                checked when written.\n");
1127 fprintf (st, "    -V          Perform a verification pass to confirm successful data copy\n");
1128 fprintf (st, "                operation.\n");
1129 fprintf (st, "    -U          Fix inconsistencies which are overridden by the -O switch\n");
1130 fprintf (st, "    -Y          Answer Yes to prompt to overwrite last track (on disk create)\n");
1131 fprintf (st, "    -N          Answer No to prompt to overwrite last track (on disk create)\n");
1132 return SCPE_OK;
1133 }
1134 
1135 t_stat sim_disk_reset (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1136 {
1137 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
1138 
1139 if (!(uptr->flags & UNIT_ATT))                          /* attached? */
1140     return SCPE_OK;
1141 
1142 sim_debug (ctx->dbit, ctx->dptr, "sim_disk_reset(unit=%lu)\n", (unsigned long)(uptr-ctx->dptr->units));
1143 
1144 _sim_disk_io_flush(uptr);
1145 return SCPE_OK;
1146 }
1147 
1148 t_stat sim_disk_perror (UNIT *uptr, const char *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
1149 {
1150 int saved_errno = errno;
1151 
1152 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
1153     return SCPE_NOATT;
1154 switch (DK_GET_FMT (uptr)) {                            /* case on format */
1155     case DKUF_F_STD:                                    /* SIMH format */
1156         perror (msg);
1157         sim_printf ("%s %s: %s (Error %d)\r\n", sim_uname(uptr), msg, xstrerror_l(saved_errno), saved_errno);
1158     /*FALLTHRU*/ /* fall through */ /* fallthrough */
1159     default:
1160         ;
1161     }
1162 return SCPE_OK;
1163 }
1164 
1165 t_stat sim_disk_clearerr (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1166 {
1167 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
1168     return SCPE_NOATT;
1169 switch (DK_GET_FMT (uptr)) {                            /* case on format */
1170     case DKUF_F_STD:                                    /* SIMH format */
1171         clearerr (uptr->fileref);
1172         break;
1173     default:
1174         ;
1175     }
1176 return SCPE_OK;
1177 }
1178 
1179 void sim_disk_data_trace(UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason)
     /* [previous][next][first][last][top][bottom][index][help] */
1180 {
1181 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
1182 
1183 if (sim_deb && (ctx->dptr->dctrl & reason)) {
1184     char pos[32];
1185 
1186     (void)sprintf (pos, "lbn: %08X ", (unsigned int)lba);
1187     sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), pos, len, txt, reason);
1188     }
1189 }

/* [previous][next][first][last][top][bottom][index][help] */