First Commit of my working state
[simh.git] / sim_tape.c
CommitLineData
196ba1fc
PH
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
70struct 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
76static 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
85extern int32 sim_switches;\r
86\r
87t_stat sim_tape_ioerr (UNIT *uptr);\r
88t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat);\r
89uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map);\r
90t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map);\r
91\r
92/* Attach tape unit */\r
93\r
94t_stat sim_tape_attach (UNIT *uptr, char *cptr)\r
95{\r
96uint32 objc;\r
97char gbuf[CBUFSIZE];\r
98t_stat r;\r
99\r
100if (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
106r = attach_unit (uptr, cptr); /* attach unit */\r
107if (r != SCPE_OK) return r; /* error? */\r
108switch (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
129sim_tape_rewind (uptr);\r
130return SCPE_OK;\r
131}\r
132\r
133/* Detach tape unit */\r
134\r
135t_stat sim_tape_detach (UNIT *uptr)\r
136{\r
137uint32 f = MT_GET_FMT (uptr);\r
138t_stat r;\r
139\r
140r = detach_unit (uptr); /* detach unit */\r
141if (r != SCPE_OK) return r;\r
142switch (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
154sim_tape_rewind (uptr);\r
155return 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
177t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc)\r
178{\r
179uint8 c;\r
180t_bool all_eof;\r
181uint32 f = MT_GET_FMT (uptr);\r
182t_mtrlnt sbc;\r
183t_tpclnt tpcbc;\r
184\r
185MT_CLR_PNU (uptr);\r
186if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */\r
187sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set tape pos */\r
188switch (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
255return 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
279t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc)\r
280{\r
281uint8 c;\r
282t_bool all_eof;\r
283uint32 f = MT_GET_FMT (uptr);\r
284t_addr ppos;\r
285t_mtrlnt sbc;\r
286t_tpclnt tpcbc;\r
287\r
288MT_CLR_PNU (uptr);\r
289if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */\r
290if (sim_tape_bot (uptr)) return MTSE_BOT; /* at BOT? */\r
291switch (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
352return 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
376t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)\r
377{\r
378uint32 f = MT_GET_FMT (uptr);\r
379t_mtrlnt i, tbc, rbc;\r
380t_addr opos;\r
381t_stat st;\r
382\r
383opos = uptr->pos; /* old position */\r
384if (st = sim_tape_rdlntf (uptr, &tbc)) return st; /* read rec lnt */\r
385*bc = rbc = MTR_L (tbc); /* strip error flag */\r
386if (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
391i = sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */\r
392if (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
397for ( ; i < rbc; i++) buf[i] = 0; /* fill with 0's */\r
398if (f == MTUF_F_P7B) buf[0] = buf[0] & P7B_DPAR; /* p7b? strip SOR */\r
399return (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
424t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max)\r
425{\r
426uint32 f = MT_GET_FMT (uptr);\r
427t_mtrlnt i, rbc, tbc;\r
428t_stat st;\r
429\r
430if (st = sim_tape_rdlntr (uptr, &tbc)) return st; /* read rec lnt */\r
431*bc = rbc = MTR_L (tbc); /* strip error flag */\r
432if (rbc > max) return MTSE_INVRL; /* rec out of range? */\r
433i = sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */\r
434if (ferror (uptr->fileref)) /* error? */\r
435 return sim_tape_ioerr (uptr);\r
436for ( ; i < rbc; i++) buf[i] = 0; /* fill with 0's */\r
437if (f == MTUF_F_P7B) buf[0] = buf[0] & P7B_DPAR; /* p7b? strip SOR */\r
438return (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
458t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc)\r
459{\r
460uint32 f = MT_GET_FMT (uptr);\r
461t_mtrlnt sbc;\r
462\r
463MT_CLR_PNU (uptr);\r
464sbc = MTR_L (bc);\r
465if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */\r
466if (sim_tape_wrp (uptr)) return MTSE_WRP; /* write prot? */\r
467if (sbc == 0) return MTSE_OK; /* nothing to do? */\r
468sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */\r
469switch (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
496return MTSE_OK;\r
497}\r
498\r
499/* Write metadata forward (internal routine) */\r
500\r
501t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat)\r
502{\r
503MT_CLR_PNU (uptr);\r
504if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */\r
505if (sim_tape_wrp (uptr)) return MTSE_WRP; /* write prot? */\r
506sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */\r
507sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref);\r
508if (ferror (uptr->fileref)) { /* error? */\r
509 MT_SET_PNU (uptr);\r
510 return sim_tape_ioerr (uptr);\r
511 }\r
512uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* move tape */\r
513return MTSE_OK;\r
514}\r
515\r
516/* Write tape mark */\r
517\r
518t_stat sim_tape_wrtmk (UNIT *uptr)\r
519{\r
520if (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
524return sim_tape_wrdata (uptr, MTR_TMK);\r
525}\r
526\r
527/* Write end of medium */\r
528\r
529t_stat sim_tape_wreom (UNIT *uptr)\r
530{\r
531if (MT_GET_FMT (uptr) == MTUF_F_P7B) return MTSE_FMT; /* cant do P7B */\r
532return 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
624t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi)\r
625{\r
626t_stat st;\r
627t_mtrlnt meta, sbc, new_len, rec_size;\r
628t_addr gap_pos = uptr->pos;\r
629uint32 file_size, marker_count;\r
630uint32 format = MT_GET_FMT (uptr);\r
631uint32 gap_alloc = 0; /* gap allocated from tape */\r
632int32 gap_needed = (gaplen * bpi) / 10; /* gap remainder still needed */\r
633const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */\r
634const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */\r
635\r
636MT_CLR_PNU (uptr);\r
637\r
638if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */\r
639 return MTSE_UNATT;\r
640if (format != MTUF_F_STD) /* not SIMH fmt? */\r
641 return MTSE_FMT;\r
642if (sim_tape_wrp (uptr)) /* write protected? */\r
643 return MTSE_WRP;\r
644\r
645file_size = sim_fsize (uptr->fileref); /* get file size */\r
646sim_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
659do {\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
733while (gap_needed > 0);\r
734\r
735uptr->pos = gap_pos; /* reposition to gap start */\r
736\r
737if (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
747marker_count = gap_alloc / meta_size; /* count of gap markers */\r
748\r
749do {\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
756while (--marker_count > 0);\r
757\r
758return MTSE_OK;\r
759}\r
760\r
761/* Space record forward */\r
762\r
763t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc)\r
764{\r
765t_stat st;\r
766\r
767st = sim_tape_rdlntf (uptr, bc); /* get record length */\r
768*bc = MTR_L (*bc);\r
769return st;\r
770}\r
771\r
772/* Space record reverse */\r
773\r
774t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc)\r
775{\r
776t_stat st;\r
777\r
778if (MT_TST_PNU (uptr)) {\r
779 MT_CLR_PNU (uptr);\r
780 *bc = 0;\r
781 return MTSE_OK;\r
782 }\r
783st = sim_tape_rdlntr (uptr, bc); /* get record length */\r
784*bc = MTR_L (*bc);\r
785return st;\r
786}\r
787\r
788/* Rewind tape */\r
789\r
790t_stat sim_tape_rewind (UNIT *uptr)\r
791{\r
792uptr->pos = 0;\r
793MT_CLR_PNU (uptr);\r
794return MTSE_OK;\r
795}\r
796\r
797/* Reset tape */\r
798\r
799t_stat sim_tape_reset (UNIT *uptr)\r
800{\r
801MT_CLR_PNU (uptr);\r
802return SCPE_OK;\r
803}\r
804\r
805/* Test for BOT */\r
806\r
807t_bool sim_tape_bot (UNIT *uptr)\r
808{\r
809uint32 f = MT_GET_FMT (uptr);\r
810\r
811return (uptr->pos <= fmts[f].bot)? TRUE: FALSE;\r
812}\r
813\r
814/* Test for end of tape */\r
815\r
816t_bool sim_tape_eot (UNIT *uptr)\r
817{\r
818return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE;\r
819}\r
820\r
821/* Test for write protect */\r
822\r
823t_bool sim_tape_wrp (UNIT *uptr)\r
824{\r
825return (uptr->flags & MTUF_WRP)? TRUE: FALSE;\r
826}\r
827\r
828/* Process I/O error */\r
829\r
830t_stat sim_tape_ioerr (UNIT *uptr)\r
831{\r
832perror ("Magtape library I/O error");\r
833clearerr (uptr->fileref);\r
834return MTSE_IOERR;\r
835}\r
836\r
837/* Set tape format */\r
838\r
839t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc)\r
840{\r
841int32 f;\r
842\r
843if (uptr == NULL) return SCPE_IERR;\r
844if (cptr == NULL) return SCPE_ARG;\r
845for (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
852return SCPE_ARG;\r
853}\r
854\r
855/* Show tape format */\r
856\r
857t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc)\r
858{\r
859int32 f = MT_GET_FMT (uptr);\r
860\r
861if (fmts[f].name) fprintf (st, "%s format", fmts[f].name);\r
862else fprintf (st, "invalid format");\r
863return SCPE_OK;\r
864}\r
865\r
866/* Map a TPC format tape image */\r
867\r
868uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map)\r
869{\r
870t_addr tpos;\r
871t_tpclnt bc;\r
872uint32 i, objc;\r
873\r
874if ((uptr == NULL) || (uptr->fileref == NULL)) return 0;\r
875for (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
883if (map) map[objc] = tpos;\r
884return objc;\r
885}\r
886\r
887/* Find the preceding record in a TPC file */\r
888\r
889t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map)\r
890{\r
891uint32 lo, hi, p;\r
892\r
893\r
894if (map == NULL) return 0;\r
895lo = 0;\r
896hi = uptr->hwmark - 1;\r
897do {\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
904while (lo <= hi);\r
905return ((p == 0)? map[p]: map[p - 1]);\r
906}\r
907\r
908/* Set tape capacity */\r
909\r
910t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc)\r
911{\r
912extern uint32 sim_taddr_64;\r
913t_addr cap;\r
914t_stat r;\r
915\r
916if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG;\r
917if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r
918cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r);\r
919if (r != SCPE_OK) return SCPE_ARG;\r
920uptr->capac = cap * ((t_addr) 1000000);\r
921return SCPE_OK;\r
922}\r
923\r
924/* Show tape capacity */\r
925\r
926t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc)\r
927{\r
928if (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
935else fprintf (st, "unlimited capacity");\r
936return SCPE_OK;\r
937}\r