| 1 | /* sim_tape.c: simulator tape support library\r |
| 2 | \r |
| 3 | Copyright (c) 1993-2007, Robert M Supnik\r |
| 4 | \r |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a\r |
| 6 | copy of this software and associated documentation files (the "Software"),\r |
| 7 | to deal in the Software without restriction, including without limitation\r |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r |
| 9 | and/or sell copies of the Software, and to permit persons to whom the\r |
| 10 | Software is furnished to do so, subject to the following conditions:\r |
| 11 | \r |
| 12 | The above copyright notice and this permission notice shall be included in\r |
| 13 | all copies or substantial portions of the Software.\r |
| 14 | \r |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r |
| 18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r |
| 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r |
| 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r |
| 21 | \r |
| 22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r |
| 23 | used in advertising or otherwise to promote the sale, use or other dealings\r |
| 24 | in this Software without prior written authorization from Robert M Supnik.\r |
| 25 | \r |
| 26 | Ultimately, this will be a place to hide processing of various tape formats,\r |
| 27 | as well as OS-specific direct hardware access.\r |
| 28 | \r |
| 29 | 23-Jan-07 JDB Fixed backspace over gap at BOT\r |
| 30 | 22-Jan-07 RMS Fixed bug in P7B format read reclnt rev (found by Rich Cornwell)\r |
| 31 | 15-Dec-06 RMS Added support for small capacity tapes\r |
| 32 | 30-Aug-06 JDB Added erase gap support\r |
| 33 | 14-Feb-06 RMS Added variable tape capacity\r |
| 34 | 23-Jan-06 JDB Fixed odd-byte-write problem in sim_tape_wrrecf\r |
| 35 | 17-Dec-05 RMS Added write support for Paul Pierce 7b format\r |
| 36 | 16-Aug-05 RMS Fixed C++ declaration and cast problems\r |
| 37 | 02-May-05 RMS Added support for Pierce 7b format\r |
| 38 | 28-Jul-04 RMS Fixed bug in writing error records (found by Dave Bryan)\r |
| 39 | RMS Fixed incorrect error codes (found by Dave Bryan)\r |
| 40 | 05-Jan-04 RMS Revised for file I/O library\r |
| 41 | 25-Apr-03 RMS Added extended file support\r |
| 42 | 28-Mar-03 RMS Added E11 and TPC format support\r |
| 43 | \r |
| 44 | Public routines:\r |
| 45 | \r |
| 46 | sim_tape_attach attach tape unit\r |
| 47 | sim_tape_detach detach tape unit\r |
| 48 | sim_tape_rdrecf read tape record forward\r |
| 49 | sim_tape_rdrecr read tape record reverse\r |
| 50 | sim_tape_wrrecf write tape record forward\r |
| 51 | sim_tape_sprecf space tape record forward\r |
| 52 | sim_tape_sprecr space tape record reverse\r |
| 53 | sim_tape_wrtmk write tape mark\r |
| 54 | sim_tape_wreom erase remainder of tape\r |
| 55 | sim_tape_wrgap write erase gap\r |
| 56 | sim_tape_rewind rewind\r |
| 57 | sim_tape_reset reset unit\r |
| 58 | sim_tape_bot TRUE if at beginning of tape\r |
| 59 | sim_tape_eot TRUE if at or beyond end of tape\r |
| 60 | sim_tape_wrp TRUE if write protected\r |
| 61 | sim_tape_set_fmt set tape format\r |
| 62 | sim_tape_show_fmt show tape format\r |
| 63 | sim_tape_set_capac set tape capacity\r |
| 64 | sim_tape_show_capac show tape capacity\r |
| 65 | */\r |
| 66 | \r |
| 67 | #include "sim_defs.h"\r |
| 68 | #include "sim_tape.h"\r |
| 69 | \r |
| 70 | struct sim_tape_fmt {\r |
| 71 | char *name; /* name */\r |
| 72 | int32 uflags; /* unit flags */\r |
| 73 | t_addr bot; /* bot test */\r |
| 74 | };\r |
| 75 | \r |
| 76 | static struct sim_tape_fmt fmts[MTUF_N_FMT] = {\r |
| 77 | { "SIMH", 0, sizeof (t_mtrlnt) - 1 },\r |
| 78 | { "E11", 0, sizeof (t_mtrlnt) - 1 },\r |
| 79 | { "TPC", UNIT_RO, sizeof (t_tpclnt) - 1 },\r |
| 80 | { "P7B", 0, 0 },\r |
| 81 | /* { "TPF", UNIT_RO, 0 }, */\r |
| 82 | { NULL, 0, 0 }\r |
| 83 | };\r |
| 84 | \r |
| 85 | extern int32 sim_switches;\r |
| 86 | \r |
| 87 | t_stat sim_tape_ioerr (UNIT *uptr);\r |
| 88 | t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat);\r |
| 89 | uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map);\r |
| 90 | t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map);\r |
| 91 | \r |
| 92 | /* Attach tape unit */\r |
| 93 | \r |
| 94 | t_stat sim_tape_attach (UNIT *uptr, char *cptr)\r |
| 95 | {\r |
| 96 | uint32 objc;\r |
| 97 | char gbuf[CBUFSIZE];\r |
| 98 | t_stat r;\r |
| 99 | \r |
| 100 | if (sim_switches & SWMASK ('F')) { /* format spec? */\r |
| 101 | cptr = get_glyph (cptr, gbuf, 0); /* get spec */\r |
| 102 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r |
| 103 | if (sim_tape_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK)\r |
| 104 | return SCPE_ARG;\r |
| 105 | }\r |
| 106 | r = attach_unit (uptr, cptr); /* attach unit */\r |
| 107 | if (r != SCPE_OK) return r; /* error? */\r |
| 108 | switch (MT_GET_FMT (uptr)) { /* case on format */\r |
| 109 | \r |
| 110 | case MTUF_F_TPC: /* TPC */\r |
| 111 | objc = sim_tape_tpc_map (uptr, NULL); /* get # objects */\r |
| 112 | if (objc == 0) { /* tape empty? */\r |
| 113 | sim_tape_detach (uptr);\r |
| 114 | return SCPE_FMT; /* yes, complain */\r |
| 115 | }\r |
| 116 | uptr->filebuf = calloc (objc + 1, sizeof (t_addr));\r |
| 117 | if (uptr->filebuf == NULL) { /* map allocated? */\r |
| 118 | sim_tape_detach (uptr);\r |
| 119 | return SCPE_MEM; /* no, complain */\r |
| 120 | }\r |
| 121 | uptr->hwmark = objc + 1; /* save map size */\r |
| 122 | sim_tape_tpc_map (uptr, (t_addr *) uptr->filebuf); /* fill map */\r |
| 123 | break;\r |
| 124 | \r |
| 125 | default:\r |
| 126 | break;\r |
| 127 | }\r |
| 128 | \r |
| 129 | sim_tape_rewind (uptr);\r |
| 130 | return SCPE_OK;\r |
| 131 | }\r |
| 132 | \r |
| 133 | /* Detach tape unit */\r |
| 134 | \r |
| 135 | t_stat sim_tape_detach (UNIT *uptr)\r |
| 136 | {\r |
| 137 | uint32 f = MT_GET_FMT (uptr);\r |
| 138 | t_stat r;\r |
| 139 | \r |
| 140 | r = detach_unit (uptr); /* detach unit */\r |
| 141 | if (r != SCPE_OK) return r;\r |
| 142 | switch (f) { /* case on format */\r |
| 143 | \r |
| 144 | case MTUF_F_TPC: /* TPC */\r |
| 145 | if (uptr->filebuf) free (uptr->filebuf); /* free map */\r |
| 146 | uptr->filebuf = NULL;\r |
| 147 | uptr->hwmark = 0;\r |
| 148 | break;\r |
| 149 | \r |
| 150 | default:\r |
| 151 | break;\r |
| 152 | }\r |
| 153 | \r |
| 154 | sim_tape_rewind (uptr);\r |
| 155 | return SCPE_OK;\r |
| 156 | }\r |
| 157 | \r |
| 158 | /* Read record length forward (internal routine)\r |
| 159 | \r |
| 160 | Inputs:\r |
| 161 | uptr = pointer to tape unit\r |
| 162 | bc = pointer to returned record length\r |
| 163 | Outputs:\r |
| 164 | status = operation status\r |
| 165 | \r |
| 166 | exit condition position\r |
| 167 | \r |
| 168 | unit unattached unchanged\r |
| 169 | read error unchanged, PNU set\r |
| 170 | end of file/medium unchanged, PNU set\r |
| 171 | tape mark updated\r |
| 172 | data record updated, sim_fread will read record forward\r |
| 173 | \r |
| 174 | See notes at "sim_tape_wrgap" regarding erase gap implementation.\r |
| 175 | */\r |
| 176 | \r |
| 177 | t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)\r |
| 178 | {\r |
| 179 | uint8 c;\r |
| 180 | t_bool all_eof;\r |
| 181 | uint32 f = MT_GET_FMT (uptr);\r |
| 182 | t_mtrlnt sbc;\r |
| 183 | t_tpclnt tpcbc;\r |
| 184 | \r |
| 185 | MT_CLR_PNU (uptr);\r |
| 186 | if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */\r |
| 187 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set tape pos */\r |
| 188 | switch (f) { /* switch on fmt */\r |
| 189 | \r |
| 190 | case MTUF_F_STD: case MTUF_F_E11:\r |
| 191 | do {\r |
| 192 | sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */\r |
| 193 | sbc = MTR_L (*bc); /* save rec lnt */\r |
| 194 | if (ferror (uptr->fileref)) { /* error? */\r |
| 195 | MT_SET_PNU (uptr); /* pos not upd */\r |
| 196 | return sim_tape_ioerr (uptr);\r |
| 197 | }\r |
| 198 | if (feof (uptr->fileref) || (*bc == MTR_EOM)) { /* eof or eom? */\r |
| 199 | MT_SET_PNU (uptr); /* pos not upd */\r |
| 200 | return MTSE_EOM;\r |
| 201 | }\r |
| 202 | uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* spc over rec lnt */\r |
| 203 | if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */\r |
| 204 | if (*bc == MTR_FHGAP) { /* half gap? */\r |
| 205 | uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space fwd */\r |
| 206 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */\r |
| 207 | }\r |
| 208 | else if (*bc != MTR_GAP)\r |
| 209 | uptr->pos = uptr->pos + sizeof (t_mtrlnt) + /* spc over record */\r |
| 210 | ((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc);\r |
| 211 | }\r |
| 212 | while ((*bc == MTR_GAP) || (*bc == MTR_FHGAP));\r |
| 213 | break;\r |
| 214 | \r |
| 215 | case MTUF_F_TPC:\r |
| 216 | sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);\r |
| 217 | *bc = tpcbc; /* save rec lnt */\r |
| 218 | if (ferror (uptr->fileref)) { /* error? */\r |
| 219 | MT_SET_PNU (uptr); /* pos not upd */\r |
| 220 | return sim_tape_ioerr (uptr);\r |
| 221 | }\r |
| 222 | if (feof (uptr->fileref)) { /* eof? */\r |
| 223 | MT_SET_PNU (uptr); /* pos not upd */\r |
| 224 | return MTSE_EOM;\r |
| 225 | }\r |
| 226 | uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */\r |
| 227 | if (tpcbc == TPC_TMK) return MTSE_TMK; /* tape mark? */\r |
| 228 | uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */\r |
| 229 | break;\r |
| 230 | \r |
| 231 | case MTUF_F_P7B:\r |
| 232 | for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */\r |
| 233 | sim_fread (&c, sizeof (uint8), 1, uptr->fileref);\r |
| 234 | if (ferror (uptr->fileref)) { /* error? */\r |
| 235 | MT_SET_PNU (uptr); /* pos not upd */\r |
| 236 | return sim_tape_ioerr (uptr);\r |
| 237 | }\r |
| 238 | if (feof (uptr->fileref)) { /* eof? */\r |
| 239 | if (sbc == 0) return MTSE_EOM; /* no data? eom */\r |
| 240 | break; /* treat like eor */\r |
| 241 | }\r |
| 242 | if ((sbc != 0) && (c & P7B_SOR)) break; /* next record? */\r |
| 243 | if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0;\r |
| 244 | }\r |
| 245 | *bc = sbc; /* save rec lnt */\r |
| 246 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */\r |
| 247 | uptr->pos = uptr->pos + sbc; /* spc over record */\r |
| 248 | if (all_eof) return MTSE_TMK; /* tape mark? */\r |
| 249 | break;\r |
| 250 | \r |
| 251 | default:\r |
| 252 | return MTSE_FMT;\r |
| 253 | }\r |
| 254 | \r |
| 255 | return MTSE_OK;\r |
| 256 | }\r |
| 257 | \r |
| 258 | /* Read record length reverse (internal routine)\r |
| 259 | \r |
| 260 | Inputs:\r |
| 261 | uptr = pointer to tape unit\r |
| 262 | bc = pointer to returned record length\r |
| 263 | Outputs:\r |
| 264 | status = operation status\r |
| 265 | \r |
| 266 | exit condition position\r |
| 267 | \r |
| 268 | unit unattached unchanged\r |
| 269 | beginning of tape unchanged\r |
| 270 | read error unchanged\r |
| 271 | end of file unchanged\r |
| 272 | end of medium updated\r |
| 273 | tape mark updated\r |
| 274 | data record updated, sim_fread will read record forward\r |
| 275 | \r |
| 276 | See notes at "sim_tape_wrgap" regarding erase gap implementation.\r |
| 277 | */\r |
| 278 | \r |
| 279 | t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)\r |
| 280 | {\r |
| 281 | uint8 c;\r |
| 282 | t_bool all_eof;\r |
| 283 | uint32 f = MT_GET_FMT (uptr);\r |
| 284 | t_addr ppos;\r |
| 285 | t_mtrlnt sbc;\r |
| 286 | t_tpclnt tpcbc;\r |
| 287 | \r |
| 288 | MT_CLR_PNU (uptr);\r |
| 289 | if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */\r |
| 290 | if (sim_tape_bot (uptr)) return MTSE_BOT; /* at BOT? */\r |
| 291 | switch (f) { /* switch on fmt */\r |
| 292 | \r |
| 293 | case MTUF_F_STD: case MTUF_F_E11:\r |
| 294 | do {\r |
| 295 | sim_fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET);\r |
| 296 | sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */\r |
| 297 | sbc = MTR_L (*bc);\r |
| 298 | if (ferror (uptr->fileref)) /* error? */\r |
| 299 | return sim_tape_ioerr (uptr);\r |
| 300 | if (feof (uptr->fileref)) return MTSE_EOM; /* eof? */\r |
| 301 | uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* spc over rec lnt */\r |
| 302 | if (*bc == MTR_EOM) return MTSE_EOM; /* eom? */\r |
| 303 | if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */\r |
| 304 | if ((*bc & MTR_M_RHGAP) == MTR_RHGAP) { /* half gap? */\r |
| 305 | uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space rev */\r |
| 306 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */\r |
| 307 | }\r |
| 308 | else if (*bc != MTR_GAP) {\r |
| 309 | uptr->pos = uptr->pos - sizeof (t_mtrlnt) - /* spc over record */\r |
| 310 | ((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc);\r |
| 311 | sim_fseek (uptr->fileref, uptr->pos + sizeof (t_mtrlnt), SEEK_SET);\r |
| 312 | }\r |
| 313 | else if (sim_tape_bot (uptr)) /* backed into BOT? */\r |
| 314 | return MTSE_BOT;\r |
| 315 | }\r |
| 316 | while ((*bc == MTR_GAP) || (*bc == MTR_RHGAP));\r |
| 317 | break;\r |
| 318 | \r |
| 319 | case MTUF_F_TPC:\r |
| 320 | ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */\r |
| 321 | sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */\r |
| 322 | sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref);\r |
| 323 | *bc = tpcbc; /* save rec lnt */\r |
| 324 | if (ferror (uptr->fileref)) /* error? */\r |
| 325 | return sim_tape_ioerr (uptr);\r |
| 326 | if (feof (uptr->fileref)) return MTSE_EOM; /* eof? */\r |
| 327 | uptr->pos = ppos; /* spc over record */\r |
| 328 | if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */\r |
| 329 | sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET);\r |
| 330 | break;\r |
| 331 | \r |
| 332 | case MTUF_F_P7B:\r |
| 333 | for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) {\r |
| 334 | sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET);\r |
| 335 | sim_fread (&c, sizeof (uint8), 1, uptr->fileref);\r |
| 336 | if (ferror (uptr->fileref)) /* error? */\r |
| 337 | return sim_tape_ioerr (uptr);\r |
| 338 | if (feof (uptr->fileref)) return MTSE_EOM; /* eof? */\r |
| 339 | if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0;\r |
| 340 | if (c & P7B_SOR) break; /* start of record? */\r |
| 341 | }\r |
| 342 | uptr->pos = uptr->pos - sbc; /* update position */\r |
| 343 | *bc = sbc; /* save rec lnt */\r |
| 344 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */\r |
| 345 | if (all_eof) return MTSE_TMK; /* tape mark? */\r |
| 346 | break;\r |
| 347 | \r |
| 348 | default:\r |
| 349 | return MTSE_FMT;\r |
| 350 | }\r |
| 351 | \r |
| 352 | return MTSE_OK;\r |
| 353 | }\r |
| 354 | \r |
| 355 | /* Read record forward\r |
| 356 | \r |
| 357 | Inputs:\r |
| 358 | uptr = pointer to tape unit\r |
| 359 | buf = pointer to buffer\r |
| 360 | bc = pointer to returned record length\r |
| 361 | max = maximum record size\r |
| 362 | Outputs:\r |
| 363 | status = operation status\r |
| 364 | \r |
| 365 | exit condition position\r |
| 366 | \r |
| 367 | unit unattached unchanged\r |
| 368 | read error unchanged, PNU set\r |
| 369 | end of file/medium unchanged, PNU set\r |
| 370 | invalid record unchanged, PNU set\r |
| 371 | tape mark updated\r |
| 372 | data record updated\r |
| 373 | data record error updated\r |
| 374 | */\r |
| 375 | \r |
| 376 | t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)\r |
| 377 | {\r |
| 378 | uint32 f = MT_GET_FMT (uptr);\r |
| 379 | t_mtrlnt i, tbc, rbc;\r |
| 380 | t_addr opos;\r |
| 381 | t_stat st;\r |
| 382 | \r |
| 383 | opos = uptr->pos; /* old position */\r |
| 384 | if (st = sim_tape_rdlntf (uptr, &tbc)) return st; /* read rec lnt */\r |
| 385 | *bc = rbc = MTR_L (tbc); /* strip error flag */\r |
| 386 | if (rbc > max) { /* rec out of range? */\r |
| 387 | MT_SET_PNU (uptr);\r |
| 388 | uptr->pos = opos;\r |
| 389 | return MTSE_INVRL;\r |
| 390 | }\r |
| 391 | i = sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */\r |
| 392 | if (ferror (uptr->fileref)) { /* error? */\r |
| 393 | MT_SET_PNU (uptr);\r |
| 394 | uptr->pos = opos;\r |
| 395 | return sim_tape_ioerr (uptr);\r |
| 396 | }\r |
| 397 | for ( ; i < rbc; i++) buf[i] = 0; /* fill with 0's */\r |
| 398 | if (f == MTUF_F_P7B) buf[0] = buf[0] & P7B_DPAR; /* p7b? strip SOR */\r |
| 399 | return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);\r |
| 400 | }\r |
| 401 | \r |
| 402 | /* Read record reverse\r |
| 403 | \r |
| 404 | Inputs:\r |
| 405 | uptr = pointer to tape unit\r |
| 406 | buf = pointer to buffer\r |
| 407 | bc = pointer to returned record length\r |
| 408 | max = maximum record size\r |
| 409 | Outputs:\r |
| 410 | status = operation status\r |
| 411 | \r |
| 412 | exit condition position\r |
| 413 | \r |
| 414 | unit unattached unchanged\r |
| 415 | read error unchanged\r |
| 416 | end of file unchanged\r |
| 417 | end of medium updated\r |
| 418 | invalid record unchanged\r |
| 419 | tape mark updated\r |
| 420 | data record updated\r |
| 421 | data record error updated\r |
| 422 | */\r |
| 423 | \r |
| 424 | t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)\r |
| 425 | {\r |
| 426 | uint32 f = MT_GET_FMT (uptr);\r |
| 427 | t_mtrlnt i, rbc, tbc;\r |
| 428 | t_stat st;\r |
| 429 | \r |
| 430 | if (st = sim_tape_rdlntr (uptr, &tbc)) return st; /* read rec lnt */\r |
| 431 | *bc = rbc = MTR_L (tbc); /* strip error flag */\r |
| 432 | if (rbc > max) return MTSE_INVRL; /* rec out of range? */\r |
| 433 | i = sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */\r |
| 434 | if (ferror (uptr->fileref)) /* error? */\r |
| 435 | return sim_tape_ioerr (uptr);\r |
| 436 | for ( ; i < rbc; i++) buf[i] = 0; /* fill with 0's */\r |
| 437 | if (f == MTUF_F_P7B) buf[0] = buf[0] & P7B_DPAR; /* p7b? strip SOR */\r |
| 438 | return (MTR_F (tbc)? MTSE_RECE: MTSE_OK);\r |
| 439 | }\r |
| 440 | \r |
| 441 | /* Write record forward\r |
| 442 | \r |
| 443 | Inputs:\r |
| 444 | uptr = pointer to tape unit\r |
| 445 | buf = pointer to buffer\r |
| 446 | bc = record length\r |
| 447 | Outputs:\r |
| 448 | status = operation status\r |
| 449 | \r |
| 450 | exit condition position\r |
| 451 | \r |
| 452 | unit unattached unchanged\r |
| 453 | write protect unchanged\r |
| 454 | write error unchanged, PNU set\r |
| 455 | data record updated\r |
| 456 | */\r |
| 457 | \r |
| 458 | t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc)\r |
| 459 | {\r |
| 460 | uint32 f = MT_GET_FMT (uptr);\r |
| 461 | t_mtrlnt sbc;\r |
| 462 | \r |
| 463 | MT_CLR_PNU (uptr);\r |
| 464 | sbc = MTR_L (bc);\r |
| 465 | if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */\r |
| 466 | if (sim_tape_wrp (uptr)) return MTSE_WRP; /* write prot? */\r |
| 467 | if (sbc == 0) return MTSE_OK; /* nothing to do? */\r |
| 468 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */\r |
| 469 | switch (f) { /* case on format */\r |
| 470 | \r |
| 471 | case MTUF_F_STD: /* standard */\r |
| 472 | sbc = MTR_L ((bc + 1) & ~1); /* pad odd length */\r |
| 473 | case MTUF_F_E11: /* E11 */\r |
| 474 | sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);\r |
| 475 | sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);\r |
| 476 | sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);\r |
| 477 | if (ferror (uptr->fileref)) { /* error? */\r |
| 478 | MT_SET_PNU (uptr);\r |
| 479 | return sim_tape_ioerr (uptr);\r |
| 480 | }\r |
| 481 | uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt)); /* move tape */\r |
| 482 | break;\r |
| 483 | \r |
| 484 | case MTUF_F_P7B: /* Pierce 7B */\r |
| 485 | buf[0] = buf[0] | P7B_SOR; /* mark start of rec */\r |
| 486 | sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref);\r |
| 487 | sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */\r |
| 488 | if (ferror (uptr->fileref)) { /* error? */\r |
| 489 | MT_SET_PNU (uptr);\r |
| 490 | return sim_tape_ioerr (uptr);\r |
| 491 | }\r |
| 492 | uptr->pos = uptr->pos + sbc; /* move tape */\r |
| 493 | break;\r |
| 494 | }\r |
| 495 | \r |
| 496 | return MTSE_OK;\r |
| 497 | }\r |
| 498 | \r |
| 499 | /* Write metadata forward (internal routine) */\r |
| 500 | \r |
| 501 | t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat)\r |
| 502 | {\r |
| 503 | MT_CLR_PNU (uptr);\r |
| 504 | if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */\r |
| 505 | if (sim_tape_wrp (uptr)) return MTSE_WRP; /* write prot? */\r |
| 506 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */\r |
| 507 | sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref);\r |
| 508 | if (ferror (uptr->fileref)) { /* error? */\r |
| 509 | MT_SET_PNU (uptr);\r |
| 510 | return sim_tape_ioerr (uptr);\r |
| 511 | }\r |
| 512 | uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* move tape */\r |
| 513 | return MTSE_OK;\r |
| 514 | }\r |
| 515 | \r |
| 516 | /* Write tape mark */\r |
| 517 | \r |
| 518 | t_stat sim_tape_wrtmk (UNIT *uptr)\r |
| 519 | {\r |
| 520 | if (MT_GET_FMT (uptr) == MTUF_F_P7B) { /* P7B? */\r |
| 521 | uint8 buf = P7B_EOF; /* eof mark */\r |
| 522 | return sim_tape_wrrecf (uptr, &buf, 1); /* write char */\r |
| 523 | }\r |
| 524 | return sim_tape_wrdata (uptr, MTR_TMK);\r |
| 525 | }\r |
| 526 | \r |
| 527 | /* Write end of medium */\r |
| 528 | \r |
| 529 | t_stat sim_tape_wreom (UNIT *uptr)\r |
| 530 | {\r |
| 531 | if (MT_GET_FMT (uptr) == MTUF_F_P7B) return MTSE_FMT; /* cant do P7B */\r |
| 532 | return sim_tape_wrdata (uptr, MTR_EOM);\r |
| 533 | }\r |
| 534 | \r |
| 535 | /* Write erase gap\r |
| 536 | \r |
| 537 | Inputs:\r |
| 538 | uptr = pointer to tape unit\r |
| 539 | gaplen = length of gap in tenths of an inch\r |
| 540 | bpi = current recording density in bytes per inch\r |
| 541 | \r |
| 542 | Outputs:\r |
| 543 | status = operation status\r |
| 544 | \r |
| 545 | exit condition position\r |
| 546 | ------------------ ------------------\r |
| 547 | unit unattached unchanged\r |
| 548 | unsupported format unchanged\r |
| 549 | write protected unchanged\r |
| 550 | read error unchanged, PNU set\r |
| 551 | write error unchanged, PNU set\r |
| 552 | gap written updated\r |
| 553 | \r |
| 554 | \r |
| 555 | An erase gap is represented in the tape image file by a special metadata\r |
| 556 | value. This value is chosen so that it is still recognizable even if it has\r |
| 557 | been "cut in half" by a subsequent data overwrite that does not end on a\r |
| 558 | metadatum-sized boundary. In addition, a range of metadata values are\r |
| 559 | reserved for detection in the reverse direction. Erase gaps are supported\r |
| 560 | only in SIMH tape format.\r |
| 561 | \r |
| 562 | This implementation supports erasing gaps in the middle of a populated tape\r |
| 563 | image and will always produce a valid image. It also produces valid images\r |
| 564 | when overwriting gaps with data records, with one exception: a data write\r |
| 565 | that leaves only two bytes of gap remaining will produce an invalid tape.\r |
| 566 | This limitation is deemed acceptable, as it is analogous to the existing\r |
| 567 | limitation that data records cannot overwrite other data records without\r |
| 568 | producing an invalid tape.\r |
| 569 | \r |
| 570 | Because SIMH tape images do not carry physical parameters (e.g., recording\r |
| 571 | density), overwriting a tape image file containing gap metadata is\r |
| 572 | problematic if the density setting is not the same as that used during\r |
| 573 | recording. There is no way to establish a gap of a certain length\r |
| 574 | unequivocally in an image file, so this implementation establishes a gap of a\r |
| 575 | certain number of bytes that reflect the desired gap length at the bpi used\r |
| 576 | during writing.\r |
| 577 | \r |
| 578 | To write an erase gap, the implementation uses one of two approaches,\r |
| 579 | depending on whether or not the current tape position is at EOM. Erasing at\r |
| 580 | EOM presents no special difficulties; gap metadata markers are written for\r |
| 581 | the prescribed number of bytes. If the tape is not at EOM, then erasing must\r |
| 582 | take into account the existing record structure to ensure that a valid tape\r |
| 583 | image is maintained.\r |
| 584 | \r |
| 585 | The general approach is to erase for the nominal number of bytes but to\r |
| 586 | increase that length, if necessary, to ensure that a partially overwritten\r |
| 587 | data record at the end of the gap can be altered to maintain validity.\r |
| 588 | Because the smallest legal tape record requires space for two metadata\r |
| 589 | markers plus two data bytes, an erasure that would leave less than that\r |
| 590 | is increased to consume the entire record. Otherwise, the final record is\r |
| 591 | truncated appropriately.\r |
| 592 | \r |
| 593 | When reading in either direction, gap metadata markers are ignored (skipped)\r |
| 594 | until a record length header, EOF marker, EOM marker, or physical EOF is\r |
| 595 | encountered. Thus, tape images containing gap metadata are transparent to\r |
| 596 | the calling simulator.\r |
| 597 | \r |
| 598 | The permissibility of data record lengths that are not multiples of the\r |
| 599 | metadatum size presents a difficulty when reading. If such an "odd length"\r |
| 600 | record is written over a gap, half of a metadata marker will exist\r |
| 601 | immediately after the trailing record length.\r |
| 602 | \r |
| 603 | This condition is detected when reading forward by the appearance of a\r |
| 604 | "reversed" marker. The value appears reversed because the value is made up\r |
| 605 | of half of one marker and half of the next. This is handled by seeking\r |
| 606 | forward two bytes to resync (the stipulation above that the overwrite cannot\r |
| 607 | leave only two bytes of gap means that at least one "whole" metadata marker\r |
| 608 | will follow). Reading in reverse presents a more complex problem, because\r |
| 609 | half of the marker is from the preceding trailing record length marker and\r |
| 610 | therefore could be any of a range of values. However, that range is\r |
| 611 | restricted by the SIMH tape specification requirement that record length\r |
| 612 | metadata values must have bits 30:24 set to zero. This allows unambiguous\r |
| 613 | detection of the condition.\r |
| 614 | \r |
| 615 | The value chosen for gap metadata and the values reserved for "half-gap"\r |
| 616 | detection are:\r |
| 617 | \r |
| 618 | 0xFFFFFFFE - primary gap value\r |
| 619 | 0xFFFEFFFF - reserved (indicates half-gap in forward reads)\r |
| 620 | 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads)\r |
| 621 | 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads)\r |
| 622 | */\r |
| 623 | \r |
| 624 | t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi)\r |
| 625 | {\r |
| 626 | t_stat st;\r |
| 627 | t_mtrlnt meta, sbc, new_len, rec_size;\r |
| 628 | t_addr gap_pos = uptr->pos;\r |
| 629 | uint32 file_size, marker_count;\r |
| 630 | uint32 format = MT_GET_FMT (uptr);\r |
| 631 | uint32 gap_alloc = 0; /* gap allocated from tape */\r |
| 632 | int32 gap_needed = (gaplen * bpi) / 10; /* gap remainder still needed */\r |
| 633 | const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */\r |
| 634 | const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */\r |
| 635 | \r |
| 636 | MT_CLR_PNU (uptr);\r |
| 637 | \r |
| 638 | if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */\r |
| 639 | return MTSE_UNATT;\r |
| 640 | if (format != MTUF_F_STD) /* not SIMH fmt? */\r |
| 641 | return MTSE_FMT;\r |
| 642 | if (sim_tape_wrp (uptr)) /* write protected? */\r |
| 643 | return MTSE_WRP;\r |
| 644 | \r |
| 645 | file_size = sim_fsize (uptr->fileref); /* get file size */\r |
| 646 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */\r |
| 647 | \r |
| 648 | /* Read tape records and allocate to gap until amount required is consumed.\r |
| 649 | \r |
| 650 | Read next metadatum from tape:\r |
| 651 | - EOF or EOM: allocate remainder of bytes needed.\r |
| 652 | - TMK or GAP: allocate sizeof(metadatum) bytes.\r |
| 653 | - Reverse GAP: allocate sizeof(metadatum) / 2 bytes.\r |
| 654 | - Data record: see below.\r |
| 655 | \r |
| 656 | Loop until bytes needed = 0.\r |
| 657 | */\r |
| 658 | \r |
| 659 | do {\r |
| 660 | sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */\r |
| 661 | \r |
| 662 | if (ferror (uptr->fileref)) { /* read error? */\r |
| 663 | uptr->pos = gap_pos; /* restore original position */\r |
| 664 | MT_SET_PNU (uptr); /* position not updated */\r |
| 665 | return sim_tape_ioerr (uptr); /* translate error */\r |
| 666 | }\r |
| 667 | else\r |
| 668 | uptr->pos = uptr->pos + meta_size; /* move tape over datum */\r |
| 669 | \r |
| 670 | if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */\r |
| 671 | gap_alloc = gap_alloc + gap_needed; /* allocate remainder */\r |
| 672 | gap_needed = 0;\r |
| 673 | }\r |
| 674 | \r |
| 675 | else if ((meta == MTR_GAP) || (meta == MTR_TMK)) { /* gap or tape mark? */\r |
| 676 | gap_alloc = gap_alloc + meta_size; /* allocate marker space */\r |
| 677 | gap_needed = gap_needed - meta_size; /* reduce requirement */\r |
| 678 | }\r |
| 679 | \r |
| 680 | else if (meta == MTR_FHGAP) { /* half gap? */\r |
| 681 | uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */\r |
| 682 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */\r |
| 683 | gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */\r |
| 684 | gap_needed = gap_needed - meta_size / 2; /* reduce requirement */\r |
| 685 | }\r |
| 686 | \r |
| 687 | else if (uptr->pos + \r |
| 688 | MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */\r |
| 689 | gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */\r |
| 690 | gap_needed = 0; /* allocate remainder */\r |
| 691 | }\r |
| 692 | \r |
| 693 | /* Allocate a data record:\r |
| 694 | - Determine record size in bytes (including metadata)\r |
| 695 | - If record size - bytes needed < smallest allowed record size,\r |
| 696 | allocate entire record to gap, else allocate needed amount and\r |
| 697 | truncate data record to reflect remainder.\r |
| 698 | */\r |
| 699 | else { /* data record */\r |
| 700 | sbc = MTR_L (meta); /* get record data length */\r |
| 701 | rec_size = ((sbc + 1) & ~1) + meta_size * 2; /* overall size in bytes */\r |
| 702 | \r |
| 703 | if (rec_size < gap_needed + min_rec_size) { /* rec too small? */\r |
| 704 | uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */\r |
| 705 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */\r |
| 706 | gap_alloc = gap_alloc + rec_size; /* allocate record */\r |
| 707 | gap_needed = gap_needed - rec_size; /* reduce requirement */\r |
| 708 | }\r |
| 709 | \r |
| 710 | else { /* record size OK */\r |
| 711 | uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */\r |
| 712 | new_len = MTR_F (meta) | (sbc - gap_needed); /* truncate to new len */\r |
| 713 | st = sim_tape_wrdata (uptr, new_len); /* write new rec len */\r |
| 714 | \r |
| 715 | if (st != MTSE_OK) { /* write OK? */\r |
| 716 | uptr->pos = gap_pos; /* restore orig pos */\r |
| 717 | return st; /* PNU was set by wrdata */\r |
| 718 | }\r |
| 719 | \r |
| 720 | uptr->pos = uptr->pos + sbc - gap_needed; /* position to end of data */\r |
| 721 | st = sim_tape_wrdata (uptr, new_len); /* write new rec len */\r |
| 722 | \r |
| 723 | if (st != MTSE_OK) { /* write OK? */\r |
| 724 | uptr->pos = gap_pos; /* restore orig pos */\r |
| 725 | return st; /* PNU was set by wrdata */\r |
| 726 | }\r |
| 727 | \r |
| 728 | gap_alloc = gap_alloc + gap_needed; /* allocate remainder */\r |
| 729 | gap_needed = 0;\r |
| 730 | }\r |
| 731 | }\r |
| 732 | }\r |
| 733 | while (gap_needed > 0);\r |
| 734 | \r |
| 735 | uptr->pos = gap_pos; /* reposition to gap start */\r |
| 736 | \r |
| 737 | if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */\r |
| 738 | st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */\r |
| 739 | if (st != MTSE_OK) { /* write OK? */\r |
| 740 | uptr->pos = gap_pos; /* restore orig pos */\r |
| 741 | return st; /* PNU was set by wrdata */\r |
| 742 | }\r |
| 743 | uptr->pos = uptr->pos - meta_size / 2; /* realign position */\r |
| 744 | gap_alloc = gap_alloc - 2; /* decrease gap to write */\r |
| 745 | }\r |
| 746 | \r |
| 747 | marker_count = gap_alloc / meta_size; /* count of gap markers */\r |
| 748 | \r |
| 749 | do {\r |
| 750 | st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */\r |
| 751 | if (st != MTSE_OK) { /* write OK? */\r |
| 752 | uptr->pos = gap_pos; /* restore orig pos */\r |
| 753 | return st; /* PNU was set by wrdata */\r |
| 754 | }\r |
| 755 | }\r |
| 756 | while (--marker_count > 0);\r |
| 757 | \r |
| 758 | return MTSE_OK;\r |
| 759 | }\r |
| 760 | \r |
| 761 | /* Space record forward */\r |
| 762 | \r |
| 763 | t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc)\r |
| 764 | {\r |
| 765 | t_stat st;\r |
| 766 | \r |
| 767 | st = sim_tape_rdlntf (uptr, bc); /* get record length */\r |
| 768 | *bc = MTR_L (*bc);\r |
| 769 | return st;\r |
| 770 | }\r |
| 771 | \r |
| 772 | /* Space record reverse */\r |
| 773 | \r |
| 774 | t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc)\r |
| 775 | {\r |
| 776 | t_stat st;\r |
| 777 | \r |
| 778 | if (MT_TST_PNU (uptr)) {\r |
| 779 | MT_CLR_PNU (uptr);\r |
| 780 | *bc = 0;\r |
| 781 | return MTSE_OK;\r |
| 782 | }\r |
| 783 | st = sim_tape_rdlntr (uptr, bc); /* get record length */\r |
| 784 | *bc = MTR_L (*bc);\r |
| 785 | return st;\r |
| 786 | }\r |
| 787 | \r |
| 788 | /* Rewind tape */\r |
| 789 | \r |
| 790 | t_stat sim_tape_rewind (UNIT *uptr)\r |
| 791 | {\r |
| 792 | uptr->pos = 0;\r |
| 793 | MT_CLR_PNU (uptr);\r |
| 794 | return MTSE_OK;\r |
| 795 | }\r |
| 796 | \r |
| 797 | /* Reset tape */\r |
| 798 | \r |
| 799 | t_stat sim_tape_reset (UNIT *uptr)\r |
| 800 | {\r |
| 801 | MT_CLR_PNU (uptr);\r |
| 802 | return SCPE_OK;\r |
| 803 | }\r |
| 804 | \r |
| 805 | /* Test for BOT */\r |
| 806 | \r |
| 807 | t_bool sim_tape_bot (UNIT *uptr)\r |
| 808 | {\r |
| 809 | uint32 f = MT_GET_FMT (uptr);\r |
| 810 | \r |
| 811 | return (uptr->pos <= fmts[f].bot)? TRUE: FALSE;\r |
| 812 | }\r |
| 813 | \r |
| 814 | /* Test for end of tape */\r |
| 815 | \r |
| 816 | t_bool sim_tape_eot (UNIT *uptr)\r |
| 817 | {\r |
| 818 | return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE;\r |
| 819 | }\r |
| 820 | \r |
| 821 | /* Test for write protect */\r |
| 822 | \r |
| 823 | t_bool sim_tape_wrp (UNIT *uptr)\r |
| 824 | {\r |
| 825 | return (uptr->flags & MTUF_WRP)? TRUE: FALSE;\r |
| 826 | }\r |
| 827 | \r |
| 828 | /* Process I/O error */\r |
| 829 | \r |
| 830 | t_stat sim_tape_ioerr (UNIT *uptr)\r |
| 831 | {\r |
| 832 | perror ("Magtape library I/O error");\r |
| 833 | clearerr (uptr->fileref);\r |
| 834 | return MTSE_IOERR;\r |
| 835 | }\r |
| 836 | \r |
| 837 | /* Set tape format */\r |
| 838 | \r |
| 839 | t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 840 | {\r |
| 841 | int32 f;\r |
| 842 | \r |
| 843 | if (uptr == NULL) return SCPE_IERR;\r |
| 844 | if (cptr == NULL) return SCPE_ARG;\r |
| 845 | for (f = 0; f < MTUF_N_FMT; f++) {\r |
| 846 | if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) {\r |
| 847 | uptr->flags = (uptr->flags & ~MTUF_FMT) |\r |
| 848 | (f << MTUF_V_FMT) | fmts[f].uflags;\r |
| 849 | return SCPE_OK;\r |
| 850 | }\r |
| 851 | }\r |
| 852 | return SCPE_ARG;\r |
| 853 | }\r |
| 854 | \r |
| 855 | /* Show tape format */\r |
| 856 | \r |
| 857 | t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc)\r |
| 858 | {\r |
| 859 | int32 f = MT_GET_FMT (uptr);\r |
| 860 | \r |
| 861 | if (fmts[f].name) fprintf (st, "%s format", fmts[f].name);\r |
| 862 | else fprintf (st, "invalid format");\r |
| 863 | return SCPE_OK;\r |
| 864 | }\r |
| 865 | \r |
| 866 | /* Map a TPC format tape image */\r |
| 867 | \r |
| 868 | uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map)\r |
| 869 | {\r |
| 870 | t_addr tpos;\r |
| 871 | t_tpclnt bc;\r |
| 872 | uint32 i, objc;\r |
| 873 | \r |
| 874 | if ((uptr == NULL) || (uptr->fileref == NULL)) return 0;\r |
| 875 | for (objc = 0, tpos = 0;; ) {\r |
| 876 | sim_fseek (uptr->fileref, tpos, SEEK_SET);\r |
| 877 | i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref);\r |
| 878 | if (i == 0) break;\r |
| 879 | if (map) map[objc] = tpos;\r |
| 880 | objc++;\r |
| 881 | tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt);\r |
| 882 | }\r |
| 883 | if (map) map[objc] = tpos;\r |
| 884 | return objc;\r |
| 885 | }\r |
| 886 | \r |
| 887 | /* Find the preceding record in a TPC file */\r |
| 888 | \r |
| 889 | t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map)\r |
| 890 | {\r |
| 891 | uint32 lo, hi, p;\r |
| 892 | \r |
| 893 | \r |
| 894 | if (map == NULL) return 0;\r |
| 895 | lo = 0;\r |
| 896 | hi = uptr->hwmark - 1;\r |
| 897 | do {\r |
| 898 | p = (lo + hi) >> 1;\r |
| 899 | if (uptr->pos == map[p])\r |
| 900 | return ((p == 0)? map[p]: map[p - 1]);\r |
| 901 | else if (uptr->pos < map[p]) hi = p - 1;\r |
| 902 | else lo = p + 1;\r |
| 903 | }\r |
| 904 | while (lo <= hi);\r |
| 905 | return ((p == 0)? map[p]: map[p - 1]);\r |
| 906 | }\r |
| 907 | \r |
| 908 | /* Set tape capacity */\r |
| 909 | \r |
| 910 | t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 911 | {\r |
| 912 | extern uint32 sim_taddr_64;\r |
| 913 | t_addr cap;\r |
| 914 | t_stat r;\r |
| 915 | \r |
| 916 | if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG;\r |
| 917 | if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r |
| 918 | cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r);\r |
| 919 | if (r != SCPE_OK) return SCPE_ARG;\r |
| 920 | uptr->capac = cap * ((t_addr) 1000000);\r |
| 921 | return SCPE_OK;\r |
| 922 | }\r |
| 923 | \r |
| 924 | /* Show tape capacity */\r |
| 925 | \r |
| 926 | t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc)\r |
| 927 | {\r |
| 928 | if (uptr->capac) {\r |
| 929 | if (uptr->capac >= (t_addr) 1000000)\r |
| 930 | fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000)));\r |
| 931 | else if (uptr->capac >= (t_addr) 1000)\r |
| 932 | fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000)));\r |
| 933 | else fprintf (st, "capacity=%dB", (uint32) uptr->capac);\r |
| 934 | }\r |
| 935 | else fprintf (st, "unlimited capacity");\r |
| 936 | return SCPE_OK;\r |
| 937 | }\r |