1 /* hp2100_mt.c: HP 2100 12559C 9-Track Magnetic Tape Unit Interface
2 
3    Copyright (c) 1993-2016, Robert M. Supnik
4    Copyright (c) 2017-2019, J. David Bryan
5 
6    Permission is hereby granted, free of charge, to any person obtaining a copy
7    of this software and associated documentation files (the "Software"), to deal
8    in the Software without restriction, including without limitation the rights
9    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10    copies of the Software, and to permit persons to whom the Software is
11    furnished to do so, subject to the following conditions:
12 
13    The above copyright notice and this permission notice shall be included in
14    all copies or substantial portions of the Software.
15 
16    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19    AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 
23    Except as contained in this notice, the names of the authors shall not be
24    used in advertising or otherwise to promote the sale, use or other dealings
25    in this Software without prior written authorization from the authors.
26 
27    MT           12559C 9-Track Magnetic Tape Unit Interface
28 
29    18-Mar-19    JDB     Reordered SCP includes
30    24-Jan-19    JDB     Removed DEV_TAPE from DEVICE flags
31    11-Jul-18    JDB     Revised I/O model
32    20-Jul-17    JDB     Removed "mtc_stopioe" variable and register
33    13-Mar-17    JDB     Deprecated LOCKED/WRITEENABLED for ATTACH -R
34    13-May-16    JDB     Modified for revised SCP API function parameter types
35    24-Dec-14    JDB     Added casts for explicit downward conversions
36    10-Jan-13    MP      Added DEV_TAPE to DEVICE flags
37    09-May-12    JDB     Separated assignments from conditional expressions
38    25-Mar-12    JDB     Removed redundant MTAB_VUN from "format" MTAB entry
39    10-Feb-12    JDB     Deprecated DEVNO in favor of SC
40    28-Mar-11    JDB     Tidied up signal handling
41    29-Oct-10    JDB     Fixed command scanning error in mtcio ioIOO handler
42    26-Oct-10    JDB     Changed I/O signal handler for revised signal model
43    04-Sep-08    JDB     Fixed missing flag after CLR command
44    02-Sep-08    JDB     Moved write enable and format commands from MTD to MTC
45    26-Jun-08    JDB     Rewrote device I/O to model backplane signals
46    28-Dec-06    JDB     Added ioCRS state to I/O decoders
47    07-Oct-04    JDB     Allow enable/disable from either device
48    14-Aug-04    RMS     Modified handling of end of medium (suggested by Dave Bryan)
49    06-Jul-04    RMS     Fixed spurious timing error after CLC (found by Dave Bryan)
50    26-Apr-04    RMS     Fixed SFS x,C and SFC x,C
51                         Implemented DMA SRQ (follows FLG)
52    21-Dec-03    RMS     Adjusted msc_ctime for TSB (from Mike Gemeny)
53    25-Apr-03    RMS     Revised for extended file support
54    28-Mar-03    RMS     Added multiformat support
55    28-Feb-03    RMS     Revised for magtape library
56    30-Sep-02    RMS     Revamped error handling
57    28-Aug-02    RMS     Added end of medium support
58    30-May-02    RMS     Widened POS to 32b
59    22-Apr-02    RMS     Added maximum record length test
60    20-Jan-02    RMS     Fixed bug on last character write
61    03-Dec-01    RMS     Added read only unit, extended SET/SHOW support
62    07-Sep-01    RMS     Moved function prototypes
63    30-Nov-00    RMS     Made variable names unique
64    04-Oct-98    RMS     V2.4 magtape format
65 
66    References:
67      - 12559A 9-Track Magnetic Tape Unit Interface Kit Operating and Service Manual
68          (12559-9001, July 1970)
69      - SIMH Magtape Representation and Handling
70          (Bob Supnik, 30-Aug-2006)
71 
72 
73    The 3030 was one of HP's earliest tape drives.  The 12559A controller
74    supported a single 800 bpi, 9-track drive, operating at 75 inches per second.
75    It had two unusual characteristics:
76 
77     - The controller accepted only one byte per I/O word, rather than packing
78       two bytes per word.
79 
80     - The drive could not read or write fewer than 12 bytes per record.
81 
82    The first behavior meant that DMA operation required the byte-unpacking
83    feature of the 12578A DMA card for the 2116 computer.  The second meant that
84    software drivers had to pad short records with blanks or nulls.
85 
86 
87    Implementation notes:
88 
89     1. The HP 3030 Magnetic Tape Subsystem diagnostic, part number 20433-60001,
90        has never been located, so this simulator has not been fully tested.  It
91        does pass a functional test under DOS-III using driver DVR22.
92 */
93 
94 
95 
96 #include "sim_defs.h"
97 #include "sim_tape.h"
98 
99 #include "hp2100_defs.h"
100 #include "hp2100_io.h"
101 
102 
103 
104 #define DB_V_SIZE       16                      /* max data buf */
105 #define DBSIZE          (1 << DB_V_SIZE)        /* max data cmd */
106 
107 /* Command - mtc_fnc */
108 
109 #define FNC_CLR         0300                    /* clear */
110 #define FNC_WC          0031                    /* write */
111 #define FNC_RC          0023                    /* read */
112 #define FNC_GAP         0011                    /* write gap */
113 #define FNC_FSR         0003                    /* forward space */
114 #define FNC_BSR         0041                    /* backward space */
115 #define FNC_REW         0201                    /* rewind */
116 #define FNC_RWS         0101                    /* rewind and offline */
117 #define FNC_WFM         0035                    /* write file mark */
118 
119 /* Status - stored in mtc_sta, (d) = dynamic */
120 
121 #define STA_LOCAL       0400                    /* local (d) */
122 #define STA_EOF         0200                    /* end of file */
123 #define STA_BOT         0100                    /* beginning of tape */
124 #define STA_EOT         0040                    /* end of tape */
125 #define STA_TIM         0020                    /* timing error */
126 #define STA_REJ         0010                    /* programming error */
127 #define STA_WLK         0004                    /* write locked (d) */
128 #define STA_PAR         0002                    /* parity error */
129 #define STA_BUSY        0001                    /* busy (d) */
130 
131 /* Interface state */
132 
133 typedef struct {
134     FLIP_FLOP  control;                         /* control flip-flop */
135     FLIP_FLOP  flag;                            /* flag flip-flop */
136     FLIP_FLOP  flag_buffer;                     /* flag buffer flip-flop */
137     } CARD_STATE;
138 
139 static CARD_STATE mtd;                          /* data per-card state */
140 static CARD_STATE mtc;                          /* command per-card state */
141 
142 
143 /* Interface local SCP support routines */
144 
145 static INTERFACE mtd_interface;
146 static INTERFACE mtc_interface;
147 
148 
149 static int32 mtc_fnc = 0;                       /* function */
150 static int32 mtc_sta = 0;                       /* status register */
151 static int32 mtc_dtf = 0;                       /* data xfer flop */
152 static int32 mtc_1st = 0;                       /* first svc flop */
153 static int32 mtc_ctime = 40;                    /* command wait */
154 static int32 mtc_gtime = 1000;                  /* gap stop time */
155 static int32 mtc_xtime = 15;                    /* data xfer time */
156 static uint8 mtxb[DBSIZE] = { 0 };              /* data buffer */
157 static t_mtrlnt mt_ptr = 0, mt_max = 0;         /* buffer ptrs */
158 static const uint32 mtc_cmd[] = {
159     FNC_WC, FNC_RC, FNC_GAP, FNC_FSR, FNC_BSR, FNC_REW, FNC_RWS, FNC_WFM };
160 static const uint32 mtc_cmd_count = sizeof (mtc_cmd) / sizeof (mtc_cmd[0]);
161 
162 static t_stat mtc_svc (UNIT *uptr);
163 static t_stat mt_reset (DEVICE *dptr);
164 static t_stat mtc_attach (UNIT *uptr, char *cptr);
165 static t_stat mtc_detach (UNIT *uptr);
166 static t_stat mt_map_err (UNIT *uptr, t_stat st);
167 static t_stat mt_clear (void);
168 
169 /* Device information blocks */
170 
171 static DIB mt_dib [] = {
172     { &mtd_interface,                                                   /* the device's I/O interface function pointer */
173       MTD,                                                              /* the device's select code (02-77) */
174       0,                                                                /* the card index */
175       "12559C 9-Track Magnetic Tape Unit Interface Data Channel",       /* the card description */
176       NULL },                                                           /* the ROM description */
177 
178     { &mtc_interface,                                                   /* the device's I/O interface function pointer */
179       MTC,                                                              /* the device's select code (02-77) */
180       0,                                                                /* the card index */
181       "12559C 9-Track Magnetic Tape Unit Interface Command Channel",    /* the card description */
182       NULL }                                                            /* the ROM description */
183     };
184 
185 #define mtd_dib             mt_dib [0]
186 #define mtc_dib             mt_dib [1]
187 
188 
189 /* Data card SCP data structures */
190 
191 
192 /* Unit list */
193 
194 static UNIT mtd_unit [] = {
195 /*           Event Routine  Unit Flags  Capacity  Delay */
196 /*           -------------  ----------  --------  ----- */
197     { UDATA (NULL,              0,         0)           }
198     };
199 
200 
201 /* Register list */
202 
203 static REG mtd_reg [] = {
204     { FLDATA (FLG, mtd.flag,    0) },
205     { FLDATA (FBF, mtd.flag_buffer, 0) },
206     { BRDATA (DBUF, mtxb, 8, 8, DBSIZE) },
207     { DRDATA (BPTR, mt_ptr, DB_V_SIZE + 1) },
208     { DRDATA (BMAX, mt_max, DB_V_SIZE + 1) },
209 
210       DIB_REGS (mtd_dib),
211 
212     { NULL }
213     };
214 
215 
216 /* Modifier list */
217 
218 static MTAB mtd_mod [] = {
219 /*    Entry Flags          Value  Print String  Match String  Validation    Display        Descriptor       */
220 /*    -------------------  -----  ------------  ------------  ------------  -------------  ---------------- */
221     { MTAB_XDV,              2u,  "SC",         "SC",         &hp_set_dib,  &hp_show_dib,  (void *) &mt_dib },
222     { MTAB_XDV | MTAB_NMO,  ~2u,  "DEVNO",      "DEVNO",      &hp_set_dib,  &hp_show_dib,  (void *) &mt_dib },
223     { 0 }
224     };
225 
226 
227 /* Debugging trace list */
228 
229 static DEBTAB mt_deb [] = {
230     { "IOBUS", TRACE_IOBUS },                   /* trace I/O bus signals and data words received and returned */
231     { NULL,    0           }
232     };
233 
234 
235 /* Device descriptor */
236 
237 DEVICE mtd_dev = {
238     "MTD",                                      /* device name */
239     mtd_unit,                                   /* unit array */
240     mtd_reg,                                    /* register array */
241     mtd_mod,                                    /* modifier array */
242     1,                                          /* number of units */
243     10,                                         /* address radix */
244     16,                                         /* address width */
245     1,                                          /* address increment */
246     8,                                          /* data radix */
247     8,                                          /* data width */
248     NULL,                                       /* examine routine */
249     NULL,                                       /* deposit routine */
250     &mt_reset,                                  /* reset routine */
251     NULL,                                       /* boot routine */
252     NULL,                                       /* attach routine */
253     NULL,                                       /* detach routine */
254     &mtd_dib,                                   /* device information block pointer */
255     DEV_DISABLE | DEV_DIS | DEV_DEBUG,          /* device flags */
256     0,                                          /* debug control flags */
257     mt_deb,                                     /* debug flag name array */
258     NULL,                                       /* memory size change routine */
259     NULL                                        /* logical device name */
260     };
261 
262 
263 /* Command card SCP data structures */
264 
265 
266 /* Unit list */
267 
268 #define UNIT_FLAGS          (UNIT_ATTABLE | UNIT_ROABLE)
269 
270 static UNIT mtc_unit [] = {
271 /*           Event Routine  Unit Flags  Capacity  Delay */
272 /*           -------------  ----------  --------  ----- */
273     { UDATA (&mtc_svc,      UNIT_FLAGS,    0)           },
274     };
275 
276 
277 /* Register list */
278 
279 static REG mtc_reg [] = {
280     { ORDATA (FNC, mtc_fnc, 8) },
281     { ORDATA (STA, mtc_sta, 9) },
282     { ORDATA (BUF, mtc_unit [0].buf, 8) },
283     { FLDATA (CTL, mtc.control, 0) },
284     { FLDATA (FLG, mtc.flag,    0) },
285     { FLDATA (FBF, mtc.flag_buffer, 0) },
286     { FLDATA (DTF, mtc_dtf, 0) },
287     { FLDATA (FSVC, mtc_1st, 0) },
288     { DRDATA (POS, mtc_unit [0].pos, T_ADDR_W), PV_LEFT },
289     { DRDATA (CTIME, mtc_ctime, 24), REG_NZ + PV_LEFT },
290     { DRDATA (GTIME, mtc_gtime, 24), REG_NZ + PV_LEFT },
291     { DRDATA (XTIME, mtc_xtime, 24), REG_NZ + PV_LEFT },
292 
293       DIB_REGS (mtc_dib),
294 
295     { NULL }
296     };
297 
298 
299 /* Modifier list.
300 
301    The LOCKED and WRITEENABLED modifiers are deprecated.  The supported method
302    of write-protecting a tape drive is to attach the tape image with the -R
303    (read-only) switch or by setting the host operating system's read-only
304    attribute on the tape image file.  This simulates removing the write ring
305    from the tape reel before mounting it on the drive.  There is no hardware
306    method of write-protecting a mounted and positioned tape reel.
307 
308 
309    Implementation notes:
310 
311     1. The UNIT_RO modifier displays "write ring" if the flag is not set.  There
312        is no corresponding entry for the opposite condition because "read only"
313        is automatically printed after the attached filename.
314 
315     2. FORMAT is really a unit option, but as there is only one unit, it is
316        specified as MTAB_XDV so that SHOW MTC FORMAT is accepted, rather than
317        requiring SHOW MTC0 FORMAT.
318 */
319 
320 static MTAB mtc_mod [] = {
321 /*    Mask Value     Match Value    Print String      Match String     Validation    Display  Descriptor */
322 /*    -------------  -------------  ----------------  ---------------  ------------  -------  ---------- */
323     { UNIT_RO,       0,             "write ring",     NULL,            NULL,         NULL,    NULL       },
324 
325     { MTUF_WLK,      0,             NULL,             "WRITEENABLED",  NULL,         NULL,    NULL       },
326     { MTUF_WLK,      MTUF_WLK,      NULL,             "LOCKED",        NULL,         NULL,    NULL       },
327 
328 
329 /*    Entry Flags          Value  Print String  Match String  Validation         Display             Descriptor       */
330 /*    -------------------  -----  ------------  ------------  -----------------  ------------------  ---------------- */
331     { MTAB_XDV,              0,   "FORMAT",     "FORMAT",     &sim_tape_set_fmt, &sim_tape_show_fmt, NULL             },
332 
333     { MTAB_XDV,              2u,  "SC",         "SC",         &hp_set_dib,       &hp_show_dib,       (void *) &mt_dib },
334     { MTAB_XDV | MTAB_NMO,  ~2u,  "DEVNO",      "DEVNO",      &hp_set_dib,       &hp_show_dib,       (void *) &mt_dib },
335     { 0 }
336     };
337 
338 
339 /* Device descriptor */
340 
341 DEVICE mtc_dev = {
342     "MTC",                                      /* device name */
343     mtc_unit,                                   /* unit array */
344     mtc_reg,                                    /* register array */
345     mtc_mod,                                    /* modifier array */
346     1,                                          /* number of units */
347     10,                                         /* address radix */
348     31,                                         /* address width */
349     1,                                          /* address increment */
350     8,                                          /* data radix */
351     8,                                          /* data width */
352     NULL,                                       /* examine routine */
353     NULL,                                       /* deposit routine */
354     &mt_reset,                                  /* reset routine */
355     NULL,                                       /* boot routine */
356     &mtc_attach,                                /* attach routine */
357     &mtc_detach,                                /* detach routine */
358     &mtc_dib,                                   /* device information block pointer */
359     DEV_DISABLE | DEV_DIS | DEV_DEBUG,          /* device flags */
360     0,                                          /* debug control flags */
361     mt_deb,                                     /* debug flag name array */
362     NULL,                                       /* memory size change routine */
363     NULL                                        /* logical device name */
364     };
365 
366 
367 
368 /* Data channel interface.
369 
370    The 12559A data channel interface has a number of non-standard features:
371 
372      - The card does not drive PRL or IRQ.
373      - The card does not respond to IAK.
374      - There is no control flip-flop; CLC resets the data transfer flip-flop.
375      - POPIO issues a CLR command and clears the flag and flag buffer flip-flops.
376      - CRS is not used.
377 
378    Implementation notes:
379 
380     1. The data channel has a flag buffer flip-flop (necessary for the proper
381        timing of the flag flip-flop), but the data channel does not interrupt,
382        so the flag buffer serves no other purpose.
383 */
384 
mtd_interface(const DIB * dibptr,INBOUND_SET inbound_signals,HP_WORD inbound_value)385 static SIGNALS_VALUE mtd_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
386 {
387 INBOUND_SIGNAL signal;
388 INBOUND_SET    working_set = inbound_signals;
389 SIGNALS_VALUE  outbound    = { ioNONE, 0 };
390 
391 while (working_set) {                                   /* while signals remain */
392     signal = IONEXTSIG (working_set);                   /*   isolate the next signal */
393 
394     switch (signal) {                                   /* dispatch the I/O signal */
395 
396         case ioCLF:                                     /* Clear Flag flip-flop */
397             mtd.flag_buffer = CLEAR;                    /* reset the flag buffer */
398             mtd.flag        = CLEAR;                    /*   and flag flip-flops */
399             break;
400 
401 
402         case ioSTF:                                     /* Set Flag flip-flop */
403             mtd.flag_buffer = SET;                      /* set the flag buffer flip-flop */
404             break;
405 
406 
407         case ioENF:                                     /* Enable Flag */
408             if (mtd.flag_buffer == SET)                 /* if the flag buffer flip-flop is set */
409                 mtd.flag = SET;                         /*   then set the flag flip-flop */
410             break;
411 
412 
413         case ioSFC:                                     /* Skip if Flag is Clear */
414             if (mtd.flag == CLEAR)                      /* if the flag flip-flop is clear */
415                 outbound.signals |= ioSKF;              /*   then assert the Skip on Flag signal */
416             break;
417 
418 
419         case ioSFS:                                     /* Skip if Flag is Set */
420             if (mtd.flag == SET)                        /* if the flag flip-flop is set */
421                 outbound.signals |= ioSKF;              /*   then assert the Skip on Flag signal */
422             break;
423 
424 
425         case ioIOI:                                     /* I/O data input */
426             outbound.value = mtc_unit [0].buf;          /* merge in return status */
427             break;
428 
429 
430         case ioIOO:                                     /* I/O data output */
431             mtc_unit [0].buf = inbound_value & D8_MASK; /* store data */
432             break;
433 
434 
435         case ioPOPIO:                                   /* Power-On Preset to I/O */
436             mt_clear ();                                /* issue CLR to controller */
437             mtd.flag_buffer = CLEAR;                    /* clear the flag buffer flip-flop */
438             break;
439 
440 
441         case ioCLC:                                     /* Clear Control flip-flop */
442             mtd.flag_buffer = CLEAR;                    /* reset the flag buffer */
443             mtd.flag        = CLEAR;                    /*   and flag flip-flops */
444 
445             mtc_dtf = 0;                                /* clr xfer flop */
446             break;
447 
448 
449         case ioSIR:                                     /* Set Interrupt Request */
450             if (mtd.flag == SET)                        /* if the flag flip-flop is set */
451                 outbound.signals |= ioSRQ;              /*   then assert SRQ */
452             break;
453 
454 
455         case ioPRH:                                         /* Priority High */
456             outbound.signals |= ioPRL | cnPRL | cnVALID;    /* PRL is tied to PRH */
457             break;
458 
459 
460         case ioSTC:                                     /* not used by this interface */
461         case ioCRS:                                     /* not used by this interface */
462         case ioIAK:                                     /* not used by this interface */
463         case ioIEN:                                     /* not used by this interface */
464         case ioEDT:                                     /* not used by this interface */
465         case ioPON:                                     /* not used by this interface */
466             break;
467         }
468 
469     IOCLEARSIG (working_set, signal);                   /* remove the current signal from the set */
470     }                                                   /*   and continue until all signals are processed */
471 
472 return outbound;                                        /* return the outbound signals and value */
473 }
474 
475 
476 /* Command channel interface.
477 
478    The 12559A command interface is reasonably standard, although POPIO clears,
479    rather than sets, the flag and flag buffer flip-flops.  One unusual feature
480    is that commands are initiated when they are output to the interface with
481    OTA/B, rather than waiting until control is set with STC.  STC simply enables
482    command-channel interrupts.
483 
484    Implementation notes:
485 
486     1. In hardware, the command channel card passes PRH to PRL.  The data card
487        actually drives PRL with the command channel's control and flag states.
488        That is, the priority chain is broken at the data card, although the
489        command card is interrupting.  This works in hardware, but we must break
490        PRL at the command card under simulation to allow the command card to
491        interrupt.
492 
493     2. In hardware, the CLR command takes 5 milliseconds to complete.  During
494        this time, the BUSY bit is set in the status word.  Under simulation, we
495        complete immediately, and the BUSY bit never sets..
496 */
497 
mtc_interface(const DIB * dibptr,INBOUND_SET inbound_signals,HP_WORD inbound_value)498 static SIGNALS_VALUE mtc_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
499 {
500 uint32         i, data;
501 int32          valid;
502 INBOUND_SIGNAL signal;
503 INBOUND_SET    working_set = inbound_signals;
504 SIGNALS_VALUE  outbound    = { ioNONE, 0 };
505 t_bool         irq_enabled = FALSE;
506 
507 while (working_set) {                                   /* while signals remain */
508     signal = IONEXTSIG (working_set);                   /*   isolate the next signal */
509 
510     switch (signal) {                                   /* dispatch the I/O signal */
511 
512         case ioCLF:                                     /* Clear Flag flip-flop */
513             mtc.flag_buffer = CLEAR;                    /* reset the flag buffer */
514             mtc.flag        = CLEAR;                    /*   and flag flip-flops */
515             break;
516 
517 
518         case ioSTF:                                     /* Set Flag flip-flop */
519             mtc.flag_buffer = SET;                      /* set the flag buffer flip-flop */
520             break;
521 
522 
523         case ioENF:                                     /* Enable Flag */
524             if (mtc.flag_buffer == SET)                 /* if the flag buffer flip-flop is set */
525                 mtc.flag = SET;                         /*   then set the flag flip-flop */
526             break;
527 
528 
529         case ioSFC:                                     /* Skip if Flag is Clear */
530             if (mtc.flag == CLEAR)                      /* if the flag flip-flop is clear */
531                 outbound.signals |= ioSKF;              /*   then assert the Skip on Flag signal */
532             break;
533 
534 
535         case ioSFS:                                     /* Skip if Flag is Set */
536             if (mtc.flag == SET)                        /* if the flag flip-flop is set */
537                 outbound.signals |= ioSKF;              /*   then assert the Skip on Flag signal */
538             break;
539 
540 
541         case ioIOI:                                     /* I/O data input */
542             outbound.value = mtc_sta & ~(STA_LOCAL | STA_WLK | STA_BUSY);
543 
544             if (mtc_unit [0].flags & UNIT_ATT) {        /* construct status */
545                 if (sim_is_active (mtc_unit))
546                     outbound.value |= STA_BUSY;
547 
548                 if (sim_tape_wrp (mtc_unit))
549                     outbound.value |= STA_WLK;
550                 }
551 
552             else
553                 outbound.value |= STA_BUSY | STA_LOCAL;
554             break;
555 
556 
557         case ioIOO:                                         /* I/O data output */
558             data = inbound_value & D8_MASK;                 /* only the lower 8 bits are connected */
559             mtc_sta = mtc_sta & ~STA_REJ;                   /* clear reject */
560 
561             if (data == FNC_CLR) {                          /* clear? */
562                 mt_clear ();                                /* send CLR to controller */
563 
564                 mtd.flag_buffer = mtd.flag = CLEAR;         /* clear data flag buffer and flag */
565                 mtc.flag_buffer = mtc.flag = SET;           /* set command flag buffer and flag */
566                 break;                                      /* command completes immediately */
567                 }
568 
569             for (i = valid = 0; i < mtc_cmd_count; i++)     /* is fnc valid? */
570                 if (data == mtc_cmd[i]) {
571                     valid = 1;
572                     break;
573                     }
574 
575             if (!valid || sim_is_active (mtc_unit) ||       /* is cmd valid? */
576                ((mtc_sta & STA_BOT) && (data == FNC_BSR)) ||
577                (sim_tape_wrp (mtc_unit) &&
578                  ((data == FNC_WC) || (data == FNC_GAP) || (data == FNC_WFM))))
579                 mtc_sta = mtc_sta | STA_REJ;
580 
581             else {
582                 sim_activate (mtc_unit, mtc_ctime);         /* start tape */
583                 mtc_fnc = data;                             /* save function */
584                 mtc_sta = STA_BUSY;                         /* unit busy */
585                 mt_ptr = 0;                                 /* init buffer ptr */
586 
587                 mtd.flag_buffer = mtd.flag = CLEAR;         /* clear data flag buffer and flag */
588                 mtc.flag_buffer = mtc.flag = CLEAR;         /*   and command flag buffer and flag */
589 
590                 mtc_1st = 1;                                /* set 1st flop */
591                 mtc_dtf = 1;                                /* set xfer flop */
592                 }
593             break;
594 
595 
596         case ioPOPIO:                                   /* Power-On Preset to I/O */
597             mtc.flag_buffer = CLEAR;                    /* clear the flag buffer flip-flop */
598             mtc.flag        = CLEAR;                    /*   and flag flip-flops */
599             break;
600 
601 
602         case ioCRS:                                     /* Control Reset */
603             mtc.control = CLEAR;                        /* clear the control flip-flop */
604             break;
605 
606 
607         case ioCLC:                                     /* Clear Control flip-flop */
608             mtc.control = CLEAR;
609             break;
610 
611 
612         case ioSTC:                                     /* Set Control flip-flop */
613             mtc.control = SET;
614             break;
615 
616 
617         case ioSIR:                                     /* Set Interrupt Request */
618             if (mtc.control & mtc.flag)                 /* if the control and flag flip-flops are set */
619                 outbound.signals |= cnVALID;            /*   then deny PRL */
620             else                                        /* otherwise */
621                 outbound.signals |= cnPRL | cnVALID;    /*   conditionally assert PRL */
622 
623             if (mtc.control & mtc.flag & mtc.flag_buffer)   /* if the control, flag, and flag buffer flip-flops are set */
624                 outbound.signals |= cnIRQ | cnVALID;        /*   then conditionally assert IRQ */
625 
626             if (mtc.flag == SET)                        /* if the flag flip-flop is set */
627                 outbound.signals |= ioSRQ;              /*   then assert SRQ */
628             break;
629 
630 
631         case ioIAK:                                     /* Interrupt Acknowledge */
632             mtc.flag_buffer = CLEAR;                    /* clear the flag buffer flip-flop */
633             break;
634 
635 
636         case ioIEN:                                     /* Interrupt Enable */
637             irq_enabled = TRUE;                         /* permit IRQ to be asserted */
638             break;
639 
640 
641         case ioPRH:                                         /* Priority High */
642             if (irq_enabled && outbound.signals & cnIRQ)    /* if IRQ is enabled and conditionally asserted */
643                 outbound.signals |= ioIRQ | ioFLG;          /*   then assert IRQ and FLG */
644 
645             if (!irq_enabled || outbound.signals & cnPRL)   /* if IRQ is disabled or PRL is conditionally asserted */
646                 outbound.signals |= ioPRL;                  /*   then assert it unconditionally */
647             break;
648 
649 
650         case ioEDT:                                     /* not used by this interface */
651         case ioPON:                                     /* not used by this interface */
652             break;
653         }
654 
655     IOCLEARSIG (working_set, signal);                   /* remove the current signal from the set */
656     }                                                   /*   and continue until all signals are processed */
657 
658 return outbound;                                        /* return the outbound signals and value */
659 }
660 
661 
662 /* Unit service
663 
664    If rewind done, reposition to start of tape, set status
665    else, do operation, set done, interrupt
666 
667    Can't be write locked, can only write lock detached unit
668 */
669 
mtc_svc(UNIT * uptr)670 static t_stat mtc_svc (UNIT *uptr)
671 {
672 t_mtrlnt tbc;
673 t_stat st, r = SCPE_OK;
674 
675 if ((mtc_unit [0].flags & UNIT_ATT) == 0) {             /* offline? */
676     mtc_sta = STA_LOCAL | STA_REJ;                      /* rejected */
677 
678     mtc.flag_buffer = SET;                              /* set the flag buffer */
679     io_assert (&mtc_dev, ioa_ENF);                      /*   and flag flip-flops */
680 
681     return SCPE_OK;
682     }
683 
684 switch (mtc_fnc) {                                      /* case on function */
685 
686     case FNC_REW:                                       /* rewind */
687         sim_tape_rewind (uptr);                         /* BOT */
688         mtc_sta = STA_BOT;                              /* update status */
689         break;
690 
691     case FNC_RWS:                                       /* rewind and offline */
692         sim_tape_rewind (uptr);                         /* clear position */
693         return sim_tape_detach (uptr);                  /* don't set cch flg */
694 
695     case FNC_WFM:                                       /* write file mark */
696         st = sim_tape_wrtmk (uptr);                     /* write tmk */
697         if (st != MTSE_OK)                              /* error? */
698             r = mt_map_err (uptr, st);                  /* map error */
699         mtc_sta = STA_EOF;                              /* set EOF status */
700         break;
701 
702     case FNC_GAP:                                       /* erase gap */
703         break;
704 
705     case FNC_FSR:                                       /* space forward */
706         st = sim_tape_sprecf (uptr, &tbc);              /* space rec fwd */
707         if (st != MTSE_OK)                              /* error? */
708             r = mt_map_err (uptr, st);                  /* map error */
709         break;
710 
711     case FNC_BSR:                                       /* space reverse */
712         st = sim_tape_sprecr (uptr, &tbc);              /* space rec rev */
713         if (st != MTSE_OK)                              /* error? */
714             r = mt_map_err (uptr, st);                  /* map error */
715         break;
716 
717     case FNC_RC:                                        /* read */
718         if (mtc_1st) {                                  /* first svc? */
719             mtc_1st = mt_ptr = 0;                       /* clr 1st flop */
720             st = sim_tape_rdrecf (uptr, mtxb, &mt_max, DBSIZE); /* read rec */
721             if (st == MTSE_RECE) mtc_sta = mtc_sta | STA_PAR;   /* rec in err? */
722             else if (st != MTSE_OK) {                   /* other error? */
723                 r = mt_map_err (uptr, st);              /* map error */
724                 if (r == SCPE_OK) {                     /* recoverable? */
725                     sim_activate (uptr, mtc_gtime);     /* sched IRG */
726                     mtc_fnc = 0;                        /* NOP func */
727                     return SCPE_OK;
728                     }
729                 break;                                  /* non-recov, done */
730                 }
731             if (mt_max < 12) {                          /* record too short? */
732                 mtc_sta = mtc_sta | STA_PAR;            /* set flag */
733                 break;
734                 }
735             }
736         if (mtc_dtf && (mt_ptr < mt_max)) {             /* more chars? */
737             if (mtd.flag) mtc_sta = mtc_sta | STA_TIM;
738             mtc_unit [0].buf = mtxb [mt_ptr++];         /* fetch next */
739 
740             mtd.flag_buffer = SET;                      /* set the flag buffer */
741             io_assert (&mtd_dev, ioa_ENF);              /*   and flag flip-flops */
742 
743             sim_activate (uptr, mtc_xtime);             /* re-activate */
744             return SCPE_OK;
745             }
746         sim_activate (uptr, mtc_gtime);                 /* schedule gap */
747         mtc_fnc = 0;                                    /* nop */
748         return SCPE_OK;
749 
750     case FNC_WC:                                                /* write */
751         if (mtc_1st) mtc_1st = 0;                       /* no xfr on first */
752         else {
753             if (mt_ptr < DBSIZE) {                      /* room in buffer? */
754                 mtxb[mt_ptr++] = (uint8) mtc_unit [0].buf;
755                 mtc_sta = mtc_sta & ~STA_BOT;           /* clear BOT */
756                 }
757             else mtc_sta = mtc_sta | STA_PAR;
758             }
759         if (mtc_dtf) {                                  /* xfer flop set? */
760             mtd.flag_buffer = SET;                      /* set the flag buffer */
761             io_assert (&mtd_dev, ioa_ENF);              /*   and flag flip-flops */
762 
763             sim_activate (uptr, mtc_xtime);             /* re-activate */
764             return SCPE_OK;
765             }
766         if (mt_ptr) {                                   /* write buffer */
767             st = sim_tape_wrrecf (uptr, mtxb, mt_ptr);  /* write */
768             if (st != MTSE_OK) {                        /* error? */
769                 r = mt_map_err (uptr, st);              /* map error */
770                 break;                                  /* done */
771                 }
772             }
773         sim_activate (uptr, mtc_gtime);                 /* schedule gap */
774         mtc_fnc = 0;                                    /* nop */
775         return SCPE_OK;
776 
777     default:                                            /* unknown */
778         break;
779         }
780 
781 mtc.flag_buffer = SET;                                  /* set the flag buffer */
782 io_assert (&mtc_dev, ioa_ENF);                          /*   and flag flip-flops */
783 mtc_sta = mtc_sta & ~STA_BUSY;                          /* not busy */
784 return r;
785 }
786 
787 /* Map tape error status */
788 
mt_map_err(UNIT * uptr,t_stat st)789 static t_stat mt_map_err (UNIT *uptr, t_stat st)
790 {
791 switch (st) {
792 
793     case MTSE_FMT:                                      /* illegal fmt */
794     case MTSE_UNATT:                                    /* unattached */
795         mtc_sta = mtc_sta | STA_REJ;                    /* reject */
796         return SCPE_IERR;                               /* never get here! */
797 
798     case MTSE_OK:                                       /* no error */
799         return SCPE_IERR;                               /* never get here! */
800 
801     case MTSE_EOM:                                      /* end of medium */
802     case MTSE_TMK:                                      /* end of file */
803         mtc_sta = mtc_sta | STA_EOF;                    /* eof */
804         break;
805 
806     case MTSE_IOERR:                                    /* IO error */
807         mtc_sta = mtc_sta | STA_PAR;                    /* error */
808         return SCPE_IOERR;
809         break;
810 
811     case MTSE_INVRL:                                    /* invalid rec lnt */
812         mtc_sta = mtc_sta | STA_PAR;
813         return SCPE_MTRLNT;
814 
815     case MTSE_RECE:                                     /* record in error */
816         mtc_sta = mtc_sta | STA_PAR;                    /* error */
817         break;
818 
819     case MTSE_BOT:                                      /* reverse into BOT */
820         mtc_sta = mtc_sta | STA_BOT;                    /* set status */
821         break;
822 
823     case MTSE_WRP:                                      /* write protect */
824         mtc_sta = mtc_sta | STA_REJ;                    /* reject */
825         break;
826         }
827 
828 return SCPE_OK;
829 }
830 
831 
832 /* Controller clear */
833 
mt_clear(void)834 static t_stat mt_clear (void)
835 {
836 t_stat st;
837 
838 if (sim_is_active (mtc_unit) &&                         /* write in prog? */
839     (mtc_fnc == FNC_WC) && (mt_ptr > 0)) {              /* yes, bad rec */
840     st = sim_tape_wrrecf (mtc_unit, mtxb, mt_ptr | MTR_ERF);
841     if (st != MTSE_OK)
842         mt_map_err (mtc_unit, st);
843     }
844 
845 if (((mtc_fnc == FNC_REW) || (mtc_fnc == FNC_RWS)) && sim_is_active (mtc_unit))
846     sim_cancel (mtc_unit);
847 
848 mtc_1st = mtc_dtf = 0;
849 mtc_sta = mtc_sta & STA_BOT;
850 
851 return SCPE_OK;
852 }
853 
854 
855 /* Reset routine */
856 
mt_reset(DEVICE * dptr)857 static t_stat mt_reset (DEVICE *dptr)
858 {
859 hp_enbdis_pair (dptr,                                   /* make pair cons */
860     (dptr == &mtd_dev) ? &mtc_dev : &mtd_dev);
861 
862 io_assert (dptr, ioa_POPIO);                            /* PRESET the device */
863 
864 mtc_fnc = 0;
865 mtc_1st = mtc_dtf = 0;
866 
867 sim_cancel (mtc_unit);                                  /* cancel activity */
868 sim_tape_reset (mtc_unit);
869 
870 if (mtc_unit [0].flags & UNIT_ATT)
871     mtc_sta = (sim_tape_bot (mtc_unit)? STA_BOT: 0) |
872               (sim_tape_wrp (mtc_unit)? STA_WLK: 0);
873 else
874     mtc_sta = STA_LOCAL | STA_BUSY;
875 
876 return SCPE_OK;
877 }
878 
879 
880 /* Attach routine */
881 
mtc_attach(UNIT * uptr,char * cptr)882 static t_stat mtc_attach (UNIT *uptr, char *cptr)
883 {
884 t_stat r;
885 
886 r = sim_tape_attach (uptr, cptr);                       /* attach unit */
887 if (r != SCPE_OK) return r;                             /* update status */
888 mtc_sta = STA_BOT;
889 return r;
890 }
891 
892 /* Detach routine */
893 
mtc_detach(UNIT * uptr)894 static t_stat mtc_detach (UNIT* uptr)
895 {
896 mtc_sta = 0;                                            /* update status */
897 return sim_tape_detach (uptr);                          /* detach unit */
898 }
899