1 /* sds_mt.c: SDS 940 magnetic tape simulator
2
3 Copyright (c) 2001-2012, 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 mt 7 track magnetic tape
27
28 19-Mar-12 RMS Fixed bug in scan function decode (Peter Schorn)
29 16-Feb-06 RMS Added tape capacity checking
30 07-Dec-04 RMS Added read-only file support
31 25-Apr-03 RMS Revised for extended file support
32 28-Mar-03 RMS Added multiformat support
33 28-Feb-03 RMS Revised for magtape library
34
35 Magnetic tapes are represented as a series of variable 8b records
36 of the form:
37
38 32b record length in bytes - exact number
39 byte 0
40 byte 1
41 :
42 byte n-2
43 byte n-1
44 32b record length in bytes - exact number
45
46 If the byte count is odd, the record is padded with an extra byte
47 of junk. File marks are represented by a single record length of 0.
48 End of tape is two consecutive end of file marks.
49 */
50
51 #include "sds_defs.h"
52 #include "sim_tape.h"
53
54 #define MT_MAXFR (32768 * 4)
55 #define MT_NUMDR 8 /* number drives */
56 #define MT_UNIT 07
57 #define botf u3 /* bot tape flag */
58 #define eotf u4 /* eot tape flag */
59
60 extern uint32 xfr_req;
61 extern int32 stop_invins, stop_invdev, stop_inviop;
62 int32 mt_inst = 0; /* saved instr */
63 int32 mt_eof = 0; /* end of file */
64 int32 mt_gap = 0; /* in gap */
65 int32 mt_skip = 0; /* skip rec */
66 int32 mt_bptr = 0; /* buf ptr */
67 int32 mt_blnt = 0; /* buf length */
68 int32 mt_ctime = 10; /* char time */
69 int32 mt_gtime = 1000; /* gap time */
70 int32 mt_stopioe = 1; /* stop on err */
71 uint8 mtxb[MT_MAXFR]; /* record buffer */
72 DSPT mt_tplt[] = { /* template */
73 { MT_NUMDR, 0 },
74 { MT_NUMDR, DEV_MTS },
75 { MT_NUMDR, DEV_OUT },
76 { MT_NUMDR, DEV_MTS+DEV_OUT },
77 { 0, 0 }
78 };
79
80 DEVICE mt_dev;
81 t_stat mt_svc (UNIT *uptr);
82 t_stat mt_reset (DEVICE *dptr);
83 t_stat mt_boot (int32 unitno, DEVICE *dptr);
84 t_stat mt_attach (UNIT *uptr, char *cptr);
85 t_stat mt_detach (UNIT *uptr);
86 t_stat mt_readrec (UNIT *uptr);
87 t_mtrlnt mt_readbc (UNIT *uptr);
88 void mt_readend (UNIT *uptr);
89 t_stat mt_wrend (uint32 dev);
90 void mt_set_err (UNIT *uptr);
91 t_stat mt (uint32 fnc, uint32 inst, uint32 *dat);
92
93 static const char sds_to_bcd[64] = {
94 012, 001, 002, 003, 004, 005, 006, 007,
95 010, 011, 012, 013, 014, 015, 016, 017,
96 060, 061, 062, 063, 064, 065, 066, 067,
97 070, 071, 072, 073, 074, 075, 076, 077,
98 040, 041, 042, 043, 044, 045, 046, 047,
99 050, 051, 052, 053, 054, 055, 056, 057,
100 020, 021, 022, 023, 024, 025, 026, 027,
101 030, 031, 032, 033, 034, 035, 036, 037
102 };
103
104 static const char bcd_to_sds[64] = {
105 000, 001, 002, 003, 004, 005, 006, 007,
106 010, 011, 000, 013, 014, 015, 016, 017,
107 060, 061, 062, 063, 064, 065, 066, 067,
108 070, 071, 072, 073, 074, 075, 076, 077,
109 040, 041, 042, 043, 044, 045, 046, 047,
110 050, 051, 052, 053, 054, 055, 056, 057,
111 020, 021, 022, 023, 024, 025, 026, 027,
112 030, 031, 032, 033, 034, 035, 036, 037
113 };
114
115 /* MT data structures
116
117 mt_dev MT device descriptor
118 mt_unit MT unit descriptor
119 mt_reg MT register list
120 */
121
122 DIB mt_dib = { CHAN_W, DEV_MT, XFR_MT0, mt_tplt, &mt };
123
124 UNIT mt_unit[] = {
125 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },
126 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },
127 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },
128 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },
129 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },
130 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },
131 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },
132 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }
133 };
134
135 REG mt_reg[] = {
136 { BRDATA (BUF, mtxb, 8, 8, MT_MAXFR) },
137 { DRDATA (BPTR, mt_bptr, 18), PV_LEFT },
138 { DRDATA (BLNT, mt_blnt, 18), PV_LEFT },
139 { FLDATA (XFR, xfr_req, XFR_V_MT0) },
140 { ORDATA (INST, mt_inst, 24) },
141 { FLDATA (EOF, mt_eof, 0) },
142 { FLDATA (GAP, mt_gap, 0) },
143 { FLDATA (SKIP, mt_skip, 0) },
144 { DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT },
145 { DRDATA (GTIME, mt_gtime, 24), REG_NZ + PV_LEFT },
146 { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0,
147 MT_NUMDR, PV_LEFT | REG_RO) },
148 { URDATA (BOT, mt_unit[0].botf, 10, 1, 0, MT_NUMDR, REG_RO) },
149 { URDATA (EOT, mt_unit[0].eotf, 10, 1, 0, MT_NUMDR, REG_RO) },
150 { FLDATA (STOP_IOE, mt_stopioe, 0) },
151 { NULL }
152 };
153
154 MTAB mt_mod[] = {
155 { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },
156 { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },
157 { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
158 &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
159 { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",
160 &sim_tape_set_capac, &sim_tape_show_capac, NULL },
161 { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
162 &set_chan, &show_chan, NULL },
163 { 0 }
164 };
165
166 DEVICE mt_dev = {
167 "MT", mt_unit, mt_reg, mt_mod,
168 MT_NUMDR, 10, 31, 1, 8, 8,
169 NULL, NULL, &mt_reset,
170 &mt_boot, &mt_attach, NULL,
171 &mt_dib, DEV_DISABLE
172 };
173
174 /* Mag tape routine
175
176 conn - inst = EOM0, dat = NULL
177 eom1 - inst = EOM1, dat = NULL
178 sks - inst = SKS, dat = ptr to result
179 disc - inst = device number, dat = NULL
180 wreor - inst = device number, dat = NULL
181 read - inst = device number, dat = ptr to data
182 write - inst = device number, dat = ptr to result
183 */
184
mt(uint32 fnc,uint32 inst,uint32 * dat)185 t_stat mt (uint32 fnc, uint32 inst, uint32 *dat)
186 {
187 int32 u = inst & MT_UNIT; /* get unit */
188 UNIT *uptr = mt_dev.units + u; /* get unit ptr */
189 int32 t, new_ch;
190 uint8 chr;
191 t_stat r;
192
193 switch (fnc) { /* case function */
194
195 case IO_CONN: /* connect */
196 new_ch = I_GETEOCH (inst); /* get new chan */
197 if (new_ch != mt_dib.chan) /* wrong chan? */
198 return SCPE_IERR;
199 if (mt_gap) { /* in gap? */
200 mt_gap = 0; /* clr gap flg */
201 sim_cancel (uptr); /* cancel timer */
202 }
203 else if (sim_is_active (uptr)) /* busy? */
204 CRETIOP;
205 uptr->eotf = 0; /* clr eot flag */
206 mt_eof = 0; /* clr eof flag */
207 mt_skip = 0; /* clr skp flag */
208 mt_bptr = mt_blnt = 0; /* init buffer */
209 if ((inst & DEV_MTS)? (CHC_GETCPW (inst) < 2): /* scn & cpw<3? */
210 (inst & CHC_REV)) /* rw & rev? */
211 return STOP_INVIOP;
212 mt_inst = inst; /* save inst */
213 if ((inst & DEV_MTS) && !(inst & DEV_OUT)) /* scanning? */
214 chan_set_flag (mt_dib.chan, CHF_SCAN); /* set chan flg */
215 xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */
216 sim_activate (uptr, mt_gtime); /* start timer */
217 break;
218
219 case IO_EOM1: /* EOM mode 1 */
220 new_ch = I_GETEOCH (inst); /* get new chan */
221 if (new_ch != mt_dib.chan) /* wrong chan? */
222 CRETIOP;
223 t = inst & 07670; /* get command */
224 if ((t == 04010) && !sim_is_active (uptr)) { /* rewind? */
225 sim_tape_rewind (uptr); /* rewind unit */
226 uptr->eotf = 0; /* clr eot */
227 uptr->botf = 1; /* set bot */
228 }
229 else if ((t == 03610) && sim_is_active (uptr) &&/* skip rec? */
230 ((mt_inst & DEV_OUT) == 0))
231 mt_skip = 1; /* set flag */
232 else CRETINS;
233 break;
234
235 case IO_DISC: /* disconnect */
236 sim_cancel (uptr); /* no more xfr's */
237 if (inst & DEV_OUT) { /* write? */
238 if ((r = mt_wrend (inst))) /* end record */
239 return r;
240 }
241 break;
242
243 case IO_WREOR: /* write eor */
244 chan_set_flag (mt_dib.chan, CHF_EOR); /* set eor flg */
245 if ((r = mt_wrend (inst))) /* end record */
246 return r;
247 mt_gap = 1; /* in gap */
248 sim_activate (uptr, mt_gtime); /* start timer */
249 break;
250
251 case IO_SKS: /* SKS */
252 new_ch = I_GETSKCH (inst); /* get chan # */
253 if (new_ch != mt_dib.chan) /* wrong chan? */
254 return SCPE_IERR;
255 if ((inst & (DEV_OUT | DEV_MTS)) == 0) { /* not sks 1n? */
256 t = I_GETSKCND (inst); /* get skip cond */
257 switch (t) { /* case sks cond */
258 case 001: /* sks 1021n */
259 *dat = 1; /* not magpak */
260 break;
261 case 002: /* sks 1041n */
262 if (!(uptr->flags & UNIT_ATT) || /* not ready */
263 sim_is_active (uptr)) *dat = 1;
264 break;
265 case 004: /* sks 1101n */
266 if (!uptr->eotf) *dat = 1; /* not EOT */
267 break;
268 case 010: /* sks 1201n */
269 if (!uptr->botf) *dat = 1; /* not BOT */
270 break;
271 case 013: /* sks 12610 */
272 if (!mt_gap) *dat = 1; /* not in gap */
273 break;
274 case 017: /* sks 13610 */
275 if (!mt_eof) *dat = 1; /* not EOF */
276 break;
277 case 020: /* sks 1401n */
278 if (!sim_tape_wrp (uptr)) *dat = 1; /* not wrp */
279 break;
280 case 031: /* sks 1621n */
281 case 033: /* sks 1661n */
282 *dat = 1; /* not 556bpi */
283 case 035: /* sks 1721n */
284 break; /* not 800bpi */
285 }
286 } /* end if */
287 break;
288
289 case IO_READ: /* read */
290 xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */
291 if (mt_blnt == 0) { /* first read? */
292 r = mt_readrec (uptr); /* get data */
293 if ((r != SCPE_OK) || (mt_blnt == 0)) /* err, inv reclnt? */
294 return r;
295 }
296 uptr->botf = 0; /* off BOT */
297 if (mt_inst & CHC_REV) /* get next rev */
298 chr = mtxb[--mt_bptr] & 077;
299 else chr = mtxb[mt_bptr++] & 077; /* get next fwd */
300 if (!(mt_inst & CHC_BIN)) /* bcd? */
301 chr = bcd_to_sds[chr];
302 *dat = chr & 077; /* give to chan */
303 if ((mt_inst & CHC_REV)? (mt_bptr <= 0): /* rev or fwd, */
304 (mt_bptr >= mt_blnt)) /* recd done? */
305 mt_readend (uptr);
306 break;
307
308 case IO_WRITE: /* write */
309 uptr->botf = 0; /* off BOT */
310 chr = (*dat) & 077;
311 xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */
312 if (!(mt_inst & CHC_BIN)) /* bcd? */
313 chr = sds_to_bcd[chr];
314 if (mt_bptr < MT_MAXFR) mtxb[mt_bptr++] = chr; /* insert in buf */
315 break;
316
317 default:
318 CRETINS;
319 }
320
321 return SCPE_OK;
322 }
323
324 /* Unit service */
325
mt_svc(UNIT * uptr)326 t_stat mt_svc (UNIT *uptr)
327 {
328 if (mt_gap) { /* gap timeout */
329 mt_gap = 0; /* clr gap flg */
330 chan_disc (mt_dib.chan); /* disc chan */
331 }
332 else if (mt_skip) /* skip record */
333 mt_readend (uptr);
334 else { /* normal xfr */
335 xfr_req = xfr_req | XFR_MT0; /* set xfr req */
336 sim_activate (uptr, mt_ctime); /* reactivate */
337 }
338 return SCPE_OK;
339 }
340
341 /* Read start (get new record) */
342
mt_readrec(UNIT * uptr)343 t_stat mt_readrec (UNIT *uptr)
344 {
345 t_mtrlnt tbc;
346 t_stat st;
347
348 if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */
349 mt_set_err (uptr); /* no, err, disc */
350 return SCPE_UNATT;
351 }
352 if (mt_inst & CHC_REV) /* reverse? */
353 st = sim_tape_rdrecr (uptr, mtxb, &tbc, MT_MAXFR); /* read rec rev */
354 else { /* no, fwd */
355 t_bool passed_eot = sim_tape_eot (uptr); /* passed EOT? */
356 st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR);
357 if (!passed_eot && sim_tape_eot (uptr)) /* just passed eot? */
358 uptr->eotf = 1;
359 }
360 if (st == MTSE_TMK) { /* tape mark? */
361 mt_eof = 1; /* set eof flag */
362 mtxb[0] = mtxb[1] = 017; /* EOR char */
363 mt_blnt = 2; /* store 2 */
364 return SCPE_OK;
365 }
366 if (st != MTSE_OK) { /* other error? */
367 mt_set_err (uptr); /* err, disc */
368 if (st == MTSE_IOERR) /* IO error? */
369 return SCPE_IOERR;
370 if (st == MTSE_INVRL) /* inv rec lnt? */
371 return SCPE_MTRLNT;
372 if (st == MTSE_EOM) /* eom? set eot */
373 uptr->eotf = 1;
374 return SCPE_OK;
375 }
376 mt_blnt = tbc; /* set buf lnt */
377 return SCPE_OK;
378 }
379
380 /* Read done (eof, end of record) */
381
mt_readend(UNIT * uptr)382 void mt_readend (UNIT *uptr)
383 {
384 sim_cancel (uptr); /* stop timer */
385 mt_skip = 0; /* clr skp flg */
386 chan_set_flag (mt_dib.chan, CHF_EOR); /* end record */
387 if (mt_eof) /* EOF? */
388 chan_disc (mt_dib.chan);
389 else {
390 mt_gap = 1; /* no, in gap */
391 sim_activate (uptr, mt_gtime); /* start timer */
392 }
393 return;
394 }
395
396 /* Write complete (end of record or disconnect) */
397
mt_wrend(uint32 dev)398 t_stat mt_wrend (uint32 dev)
399 {
400 UNIT *uptr = mt_dev.units + (dev & MT_UNIT);
401 t_mtrlnt tbc;
402 t_stat st;
403
404 sim_cancel (uptr); /* no more xfr's */
405 if (mt_bptr == 0) /* buf empty? */
406 return SCPE_OK;
407 if (!(uptr->flags & UNIT_ATT)) { /* attached? */
408 mt_set_err (uptr); /* no, err, disc */
409 return SCPE_UNATT;
410 }
411 if (sim_tape_wrp (uptr)) { /* write lock? */
412 mt_set_err (uptr); /* yes, err, disc */
413 return SCPE_OK;
414 }
415 if (dev & DEV_MTS) { /* erase? */
416 if (mt_inst & CHC_REV) /* reverse? */
417 sim_tape_sprecr (uptr, &tbc); /* backspace */
418 st = sim_tape_wreom (uptr); /* write eom */
419 }
420 else {
421 t_bool passed_eot = sim_tape_eot (uptr); /* passed EOT? */
422 if ((mt_bptr == 1) && (mtxb[0] == 017) && /* wr eof? */
423 ((mt_inst & 01670) == 00050))
424 st = sim_tape_wrtmk (uptr); /* write tape mark */
425 else st = sim_tape_wrrecf (uptr, mtxb, mt_bptr); /* write record */
426 if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */
427 uptr->eotf = 1;
428 }
429 mt_bptr = 0;
430 if (st != MTSE_OK) /* error? */
431 mt_set_err (uptr);
432 if (st == MTSE_IOERR)
433 return SCPE_IOERR;
434 return SCPE_OK;
435 }
436
437 /* Fatal error */
438
mt_set_err(UNIT * uptr)439 void mt_set_err (UNIT *uptr)
440 {
441 chan_set_flag (mt_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */
442 chan_disc (mt_dib.chan); /* disconnect */
443 xfr_req = xfr_req & ~XFR_MT0; /* clear xfr */
444 sim_cancel (uptr); /* stop */
445 mt_bptr = 0; /* buf empty */
446 return;
447 }
448 /* Reset routine */
449
mt_reset(DEVICE * dptr)450 t_stat mt_reset (DEVICE *dptr)
451 {
452 int32 i;
453
454 chan_disc (mt_dib.chan); /* disconnect */
455 mt_eof = 0; /* clear state */
456 mt_gap = 0;
457 mt_skip = 0;
458 mt_inst = 0;
459 mt_bptr = mt_blnt = 0;
460 xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */
461 for (i = 0; i < MT_NUMDR; i++) { /* deactivate */
462 sim_cancel (&mt_unit[i]);
463 sim_tape_reset (&mt_unit[i]);
464 mt_unit[i].eotf = 0;
465 }
466 return SCPE_OK;
467 }
468
469 /* Attach and detach routines */
470
mt_attach(UNIT * uptr,char * cptr)471 t_stat mt_attach (UNIT *uptr, char *cptr)
472 {
473 t_stat r;
474
475 r = sim_tape_attach (uptr, cptr);
476 if (r != SCPE_OK)
477 return r;
478 uptr->botf = 1;
479 uptr->eotf = 0;
480 return SCPE_OK;
481 }
482
mt_detach(UNIT * uptr)483 t_stat mt_detach (UNIT *uptr)
484 {
485 uptr->botf = uptr->eotf = 0;
486 return sim_tape_detach (uptr);
487 }
488
489 /* Boot routine - simulate FILL console command */
490
mt_boot(int32 unitno,DEVICE * dptr)491 t_stat mt_boot (int32 unitno, DEVICE *dptr)
492 {
493 extern uint32 P, M[];
494
495 if (unitno) /* only unit 0 */
496 return SCPE_ARG;
497 M[0] = 077777771; /* -7B */
498 M[1] = 007100000; /* LDX 0 */
499 M[2] = 000203610; /* EOM 3610B */
500 M[3] = 003200002; /* WIM 2 */
501 M[4] = 000100002; /* BRU 2 */
502 P = 1; /* start at 1 */
503 return SCPE_OK;
504 }
505