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