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