Commit | Line | Data |
---|---|---|
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 | |
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 |