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