1 /* pdp11_tu.c - PDP-11 TM02/TU16 TM03/TU45/TU77 Massbus magnetic tape controller
2 
3    Copyright (c) 1993-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    tu           TM02/TM03 magtape
27 
28    18-Apr-11    MP      Fixed t_addr printouts for 64b big-endian systems
29    17-May-07    RMS     CS1 DVA resides in device, not MBA
30    29-Apr-07    RMS     Fixed bug in setting FCE on TMK Naoki Hamada)
31    16-Feb-06    RMS     Added tape capacity checking
32    12-Nov-05    RMS     Changed default formatter to TM03 (for VMS)
33    31-Oct-05    RMS     Fixed address width for large files
34    16-Aug-05    RMS     Fixed C++ declaration and cast problems
35    31-Mar-05    RMS     Fixed inaccuracies in error reporting
36    18-Mar-05    RMS     Added attached test to detach routine
37    10-Sep-04    RMS     Cloned from pdp10_tu.c
38 
39    Magnetic tapes are represented as a series of variable 8b records
40    of the form:
41 
42         32b record length in bytes - exact number, sign = error
43         byte 0
44         byte 1
45         :
46         byte n-2
47         byte n-1
48         32b record length in bytes - exact number, sign = error
49 
50    If the byte count is odd, the record is padded with an extra byte
51    of junk.  File marks are represented by a single record length of 0.
52    End of tape is two consecutive end of file marks.
53 */
54 
55 #if defined (VM_PDP10)
56 #error "PDP-10 uses pdp10_tu.c!"
57 
58 #elif defined (VM_PDP11)
59 #include "pdp11_defs.h"
60 #define DEV_DIS_INIT    DEV_DIS
61 
62 #elif defined (VM_VAX)
63 #include "vax_defs.h"
64 #define DEV_DIS_INIT    0
65 #if (!UNIBUS)
66 #error "Qbus not supported!"
67 #endif
68 
69 #endif
70 #include "sim_tape.h"
71 
72 #define TU_NUMFM        1                               /* #formatters */
73 #define TU_NUMDR        8                               /* #drives */
74 #define USTAT           u3                              /* unit status */
75 #define UDENS           u4                              /* unit density */
76 #define  UD_UNK         0                               /* unknown */
77 #define MT_MAXFR        (1 << 16)                       /* max data buf */
78 #define DEV_V_TM03      (DEV_V_FFUF + 0)                /* TM02/TM03 */
79 #define DEV_TM03        (1 << DEV_V_TM03)
80 #define UNIT_V_TYPE     (MTUF_V_UF + 0)
81 #define UNIT_M_TYPE     03
82 #define UNIT_TYPE       (UNIT_M_TYPE << UNIT_V_TYPE)
83 #define UNIT_TE16       (0 << UNIT_V_TYPE)
84 #define UNIT_TU45       (1 << UNIT_V_TYPE)
85 #define UNIT_TU77       (2 << UNIT_V_TYPE)
86 #define GET_TYPE(x)     (((x) >> UNIT_V_TYPE) & UNIT_M_TYPE)
87 
88 /* CS1 - offset 0 */
89 
90 #define CS1_OF          0
91 #define CS1_GO          CSR_GO                          /* go */
92 #define CS1_V_FNC       1                               /* function pos */
93 #define CS1_M_FNC       037                             /* function mask */
94 #define CS1_N_FNC       (CS1_M_FNC + 1)
95 #define  FNC_NOP        000                             /* no operation */
96 #define  FNC_UNLOAD     001                             /* unload */
97 #define  FNC_REWIND     003                             /* rewind */
98 #define  FNC_FCLR       004                             /* formatter clear */
99 #define  FNC_RIP        010                             /* read in preset */
100 #define  FNC_ERASE      012                             /* erase tape */
101 #define  FNC_WREOF      013                             /* write tape mark */
102 #define  FNC_SPACEF     014                             /* space forward */
103 #define  FNC_SPACER     015                             /* space reverse */
104 #define FNC_XFER        024                             /* >=? data xfr */
105 #define  FNC_WCHKF      024                             /* write check */
106 #define  FNC_WCHKR      027                             /* write check rev */
107 #define  FNC_WRITE      030                             /* write */
108 #define  FNC_READF      034                             /* read forward */
109 #define  FNC_READR      037                             /* read reverse */
110 #define CS1_RW          077
111 #define CS1_DVA         04000                           /* drive avail */
112 #define GET_FNC(x)      (((x) >> CS1_V_FNC) & CS1_M_FNC)
113 
114 /* TUFS - formatter status - offset 1
115    + indicates kept in drive status
116    ^ indicates calculated on the fly
117 */
118 
119 #define FS_OF           1
120 #define FS_SAT          0000001                         /* slave attention */
121 #define FS_BOT          0000002                         /* ^beginning of tape */
122 #define FS_TMK          0000004                         /* end of file */
123 #define FS_ID           0000010                         /* ID burst detected */
124 #define FS_SLOW         0000020                         /* slowing down NI */
125 #define FS_PE           0000040                         /* ^PE status */
126 #define FS_SSC          0000100                         /* slave stat change */
127 #define FS_RDY          0000200                         /* ^formatter ready */
128 #define FS_FPR          0000400                         /* formatter present */
129 #define FS_EOT          0002000                         /* +end of tape */
130 #define FS_WRL          0004000                         /* ^write locked */
131 #define FS_MOL          0010000                         /* ^medium online */
132 #define FS_PIP          0020000                         /* +pos in progress */
133 #define FS_ERR          0040000                         /* ^error */
134 #define FS_ATA          0100000                         /* attention active */
135 #define FS_REW          0200000                         /* +rewinding */
136 
137 #define FS_DYN          (FS_ERR | FS_PIP | FS_MOL | FS_WRL | FS_EOT | \
138                          FS_RDY | FS_PE | FS_BOT)
139 
140 /* TUER - error register - offset 2 */
141 
142 #define ER_OF           2
143 #define ER_ILF          0000001                         /* illegal func */
144 #define ER_ILR          0000002                         /* illegal register */
145 #define ER_RMR          0000004                         /* reg mod refused */
146 #define ER_MCP          0000010                         /* Mbus cpar err NI */
147 #define ER_FER          0000020                         /* format sel err */
148 #define ER_MDP          0000040                         /* Mbus dpar err NI */
149 #define ER_VPE          0000100                         /* vert parity err */
150 #define ER_CRC          0000200                         /* CRC err NI */
151 #define ER_NSG          0000400                         /* non std gap err NI */
152 #define ER_FCE          0001000                         /* frame count err */
153 #define ER_ITM          0002000                         /* inv tape mark NI */
154 #define ER_NXF          0004000                         /* wlock or fnc err */
155 #define ER_DTE          0010000                         /* time err NI */
156 #define ER_OPI          0020000                         /* op incomplete */
157 #define ER_UNS          0040000                         /* drive unsafe */
158 #define ER_DCK          0100000                         /* data check NI */
159 
160 /* TUMR - maintenance register - offset 03 */
161 
162 #define MR_OF           3
163 #define MR_RW           0177637                         /* read/write */
164 
165 /* TUAS - attention summary - offset 4 */
166 
167 #define AS_OF           4
168 #define AS_U0           0000001                         /* unit 0 flag */
169 
170 /* TUFC - offset 5 */
171 
172 #define FC_OF           5
173 
174 /* TUDT - drive type - offset 6 */
175 
176 #define DT_OF           6
177 #define DT_NSA          0100000                         /* not sect addr */
178 #define DT_TAPE         0040000                         /* tape */
179 #define DT_PRES         0002000                         /* slave present */
180 #define DT_TM03         0000040                         /* TM03 formatter */
181 #define DT_OFF          0000010                         /* drive off */
182 #define DT_TU16         0000011                         /* TE16 */
183 #define DT_TU45         0000012                         /* TU45 */
184 #define DT_TU77         0000014                         /* TU77 */
185 
186 /* TUCC - check character, read only - offset 7 */
187 
188 #define CC_OF           7
189 #define CC_MBZ          0177000                         /* must be zero */
190 
191 /* TUSN - serial number - offset 8 */
192 
193 #define SN_OF           8
194 
195 /* TUTC - tape control register - offset 9 */
196 
197 #define TC_OF           9
198 #define TC_V_UNIT       0                               /* unit select */
199 #define TC_M_UNIT       07
200 #define TC_V_EVN        0000010                         /* even parity */
201 #define TC_V_FMT        4                               /* format select */
202 #define TC_M_FMT        017
203 #define  TC_STD          014                            /* standard */
204 #define  TC_CDUMP        015                            /* core dump */
205 #define TC_V_DEN        8                               /* density select */
206 #define TC_M_DEN        07
207 #define  TC_800          3                              /* 800 bpi */
208 #define  TC_1600         4                              /* 1600 bpi */
209 #define TC_AER          0010000                         /* abort on error */
210 #define TC_SAC          0020000                         /* slave addr change */
211 #define TC_FCS          0040000                         /* frame count status */
212 #define TC_ACC          0100000                         /* accelerating NI */
213 #define TC_RW           0013777
214 #define TC_MBZ          0004000
215 #define TC_RIP          ((TC_800 << TC_V_DEN) | (TC_STD << TC_V_FMT))
216 #define GET_DEN(x)      (((x) >> TC_V_DEN) & TC_M_DEN)
217 #define GET_FMT(x)      (((x) >> TC_V_FMT) & TC_M_FMT)
218 #define GET_DRV(x)      (((x) >> TC_V_UNIT) & TC_M_UNIT)
219 
220 int32 tucs1 = 0;                                        /* control/status 1 */
221 int32 tufc = 0;                                         /* frame count */
222 int32 tufs = 0;                                         /* formatter status */
223 int32 tuer = 0;                                         /* error status */
224 int32 tucc = 0;                                         /* check character */
225 int32 tumr = 0;                                         /* maint register */
226 int32 tutc = 0;                                         /* tape control */
227 int32 tu_time = 10;                                     /* record latency */
228 int32 tu_stopioe = 1;                                   /* stop on error */
229 static uint8 *xbuf = NULL;                              /* xfer buffer */
230 static uint16 *wbuf = NULL;
231 static int32 fmt_test[16] = {                           /* fmt valid */
232     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0
233     };
234 static int32 dt_map[3] = { DT_TU16, DT_TU45, DT_TU77 };
235 static char *tu_fname[CS1_N_FNC] = {
236     "NOP", "UNLD", "2", "REW", "FCLR", "5", "6", "7",
237     "RIP", "11", "ERASE", "WREOF", "SPCF", "SPCR", "16", "17",
238     "20", "21", "22", "23", "WRCHKF", "25", "26", "WRCHKR",
239     "WRITE", "31", "32", "33", "READF", "35", "36" "READR"
240     };
241 
242 extern int32 sim_switches;
243 extern FILE *sim_deb;
244 
245 t_stat tu_mbrd (int32 *data, int32 PA, int32 fmtr);
246 t_stat tu_mbwr (int32 data, int32 PA, int32 fmtr);
247 t_stat tu_svc (UNIT *uptr);
248 t_stat tu_reset (DEVICE *dptr);
249 t_stat tu_attach (UNIT *uptr, char *cptr);
250 t_stat tu_detach (UNIT *uptr);
251 t_stat tu_boot (int32 unitno, DEVICE *dptr);
252 t_stat tu_set_fmtr (UNIT *uptr, int32 val, char *cptr, void *desc);
253 t_stat tu_show_fmtr (FILE *st, UNIT *uptr, int32 val, void *desc);
254 t_stat tu_go (int32 drv);
255 int32 tu_abort (void);
256 void tu_set_er (int32 flg);
257 void tu_clr_as (int32 mask);
258 void tu_update_fs (int32 flg, int32 drv);
259 t_stat tu_map_err (int32 drv, t_stat st, t_bool qdt);
260 
261 /* TU data structures
262 
263    tu_dev       TU device descriptor
264    tu_unit      TU unit list
265    tu_reg       TU register list
266    tu_mod       TU modifier list
267 */
268 
269 DIB tu_dib = { MBA_TU, 0, &tu_mbrd, &tu_mbwr,0, 0, 0, { &tu_abort } };
270 
271 UNIT tu_unit[] = {
272     { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
273     { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
274     { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
275     { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
276     { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
277     { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
278     { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
279     { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }
280     };
281 
282 REG tu_reg[] = {
283     { GRDATA (CS1, tucs1, DEV_RDX, 6, 0) },
284     { GRDATA (FC, tufc, DEV_RDX, 16, 0) },
285     { GRDATA (FS, tufs, DEV_RDX, 16, 0) },
286     { GRDATA (ER, tuer, DEV_RDX, 16, 0) },
287     { GRDATA (CC, tucc, DEV_RDX, 16, 0) },
288     { GRDATA (MR, tumr, DEV_RDX, 16, 0) },
289     { GRDATA (TC, tutc, DEV_RDX, 16, 0) },
290     { FLDATA (STOP_IOE, tu_stopioe, 0) },
291     { DRDATA (TIME, tu_time, 24), PV_LEFT },
292     { URDATA (UST, tu_unit[0].USTAT, DEV_RDX, 17, 0, TU_NUMDR, 0) },
293     { URDATA (POS, tu_unit[0].pos, 10, T_ADDR_W, 0,
294               TU_NUMDR, PV_LEFT | REG_RO) },
295     { NULL }
296     };
297 
298 MTAB tu_mod[] = {
299     { MTAB_XTD|MTAB_VDV, 0, "MASSBUS", "MASSBUS", NULL, &mba_show_num },
300 #if defined (VM_PDP11)
301     { MTAB_XTD|MTAB_VDV, 0, "FORMATTER", "TM02",
302       &tu_set_fmtr, &tu_show_fmtr },
303     { MTAB_XTD|MTAB_VDV, 1, NULL, "TM03",
304       &tu_set_fmtr, NULL },
305 #else
306     { MTAB_XTD|MTAB_VDV, 0, "FORMATTER", NULL,
307       NULL, &tu_show_fmtr },
308 #endif
309     { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },
310     { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },
311     { UNIT_TYPE, UNIT_TE16, "TE16", "TE16", NULL },
312     { UNIT_TYPE, UNIT_TU45, "TU45", "TU45", NULL },
313     { UNIT_TYPE, UNIT_TU77, "TU77", "TU77", NULL },
314     { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
315       &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
316     { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",
317       &sim_tape_set_capac, &sim_tape_show_capac, NULL },
318     { 0 }
319     };
320 
321 DEVICE tu_dev = {
322     "TU", tu_unit, tu_reg, tu_mod,
323     TU_NUMDR, 10, T_ADDR_W, 1, DEV_RDX, 8,
324     NULL, NULL, &tu_reset,
325     &tu_boot, &tu_attach, &tu_detach,
326     &tu_dib, DEV_MBUS|DEV_UBUS|DEV_QBUS|DEV_DEBUG|DEV_DISABLE|DEV_DIS_INIT|DEV_TM03
327     };
328 
329 /* Massbus register read */
330 
tu_mbrd(int32 * data,int32 ofs,int32 fmtr)331 t_stat tu_mbrd (int32 *data, int32 ofs, int32 fmtr)
332 {
333 int32 drv;
334 
335 if (fmtr != 0) {                                        /* only one fmtr */
336     *data = 0;
337     return MBE_NXD;
338     }
339 drv = GET_DRV (tutc);                                   /* get current unit */
340 tu_update_fs (0, drv);                                  /* update status */
341 
342 switch (ofs) {                                          /* decode offset */
343 
344     case CS1_OF:                                        /* MTCS1 */
345         *data = (tucs1 & CS1_RW) | CS1_DVA;             /* DVA always set */
346         break;
347 
348     case FC_OF:                                         /* MTFC */
349         *data = tufc;
350         break;
351 
352     case FS_OF:                                         /* MTFS */
353         *data = tufs & 0177777;                         /* mask off rewind */
354         break;
355 
356     case ER_OF:                                         /* MTER */
357         *data = tuer;
358         break;
359 
360     case AS_OF:                                         /* MTAS */
361         *data = (tufs & FS_ATA)? AS_U0: 0;
362         break;
363 
364     case CC_OF:                                         /* MTCC */
365         *data = tucc = tucc & ~CC_MBZ;
366         break;
367 
368     case MR_OF:                                         /* MTMR */
369         *data = tumr;
370         break;
371 
372     case DT_OF:                                         /* MTDT */
373         *data = DT_NSA | DT_TAPE |                      /* fmtr flags */
374             ((tu_dev.flags & DEV_TM03)? DT_TM03: 0);
375         if (tu_unit[drv].flags & UNIT_DIS)
376             *data |= DT_OFF;
377         else *data |= DT_PRES | dt_map[GET_TYPE (tu_unit[drv].flags)];
378         break;
379 
380     case SN_OF:                                         /* MTSN */
381         *data = (tu_unit[drv].flags & UNIT_DIS)? 0: 040 | (drv + 1);
382         break;
383 
384     case TC_OF:                                         /* MTTC */
385         *data = tutc = tutc & ~TC_MBZ;
386         break;
387 
388     default:                                            /* all others */
389         return MBE_NXR;
390         }
391 
392 return SCPE_OK;
393 }
394 
395 /* Massbus register write */
396 
tu_mbwr(int32 data,int32 ofs,int32 fmtr)397 t_stat tu_mbwr (int32 data, int32 ofs, int32 fmtr)
398 {
399 int32 drv;
400 
401 if (fmtr != 0)                                          /* only one fmtr */
402     return MBE_NXD;
403 drv = GET_DRV (tutc);                                   /* get current unit */
404 
405 switch (ofs) {                                          /* decode PA<4:1> */
406 
407     case CS1_OF:                                        /* MTCS1 */
408         if (tucs1 & CS1_GO)
409             tu_set_er (ER_RMR);
410         else {
411             tucs1 = data & CS1_RW;
412             if (tucs1 & CS1_GO)
413                 return tu_go (drv);
414             }
415         break;
416 
417     case FC_OF:                                         /* MTFC */
418         if (tucs1 & CS1_GO)
419             tu_set_er (ER_RMR);
420         else {
421             tufc = data;
422             tutc = tutc | TC_FCS;                       /* set fc flag */
423             }
424         break;
425 
426     case AS_OF:                                         /* MTAS */
427         tu_clr_as (data);
428         break;
429 
430     case MR_OF:                                         /* MTMR */
431         tumr = (tumr & ~MR_RW) | (data & MR_RW);
432         break;
433 
434     case TC_OF:                                         /* MTTC */
435         if (tucs1 & CS1_GO)
436             tu_set_er (ER_RMR);
437         else {
438             tutc = (tutc & ~TC_RW) | (data & TC_RW) | TC_SAC;
439             drv = GET_DRV (tutc);
440             }
441         break;
442 
443     case FS_OF:                                         /* MTFS */
444     case ER_OF:                                         /* MTER */
445     case CC_OF:                                         /* MTCC */
446     case DT_OF:                                         /* MTDT */
447     case SN_OF:                                         /* MTSN */
448         if (tucs1 & CS1_GO)
449             tu_set_er (ER_RMR);
450         break;                                          /* read only */
451 
452     default:                                            /* all others */
453         return MBE_NXR;
454         }                                               /* end switch */
455 
456 tu_update_fs (0, drv);
457 return SCPE_OK;
458 }
459 
460 /* New magtape command */
461 
tu_go(int32 drv)462 t_stat tu_go (int32 drv)
463 {
464 int32 fnc, den;
465 UNIT *uptr;
466 
467 fnc = GET_FNC (tucs1);                                  /* get function */
468 den = GET_DEN (tutc);                                   /* get density */
469 uptr = tu_dev.units + drv;                              /* get unit */
470 if (DEBUG_PRS (tu_dev)) {
471     fprintf (sim_deb, ">>TU%d STRT: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=",
472              drv, tu_fname[fnc], tufc, tufs, tuer);
473     fprint_val (sim_deb, uptr->pos, 10, T_ADDR_W, PV_LEFT);
474     fprintf (sim_deb, "\n");
475     }
476 if ((fnc != FNC_FCLR) &&                                /* not clear & err */
477     ((tufs & FS_ERR) || sim_is_active (uptr))) {        /* or in motion? */
478     tu_set_er (ER_ILF);                                 /* set err */
479     tucs1 = tucs1 & ~CS1_GO;                            /* clear go */
480     tu_update_fs (FS_ATA, drv);                         /* set attn */
481     return MBE_GOE;
482     }
483 tu_clr_as (AS_U0);                                      /* clear ATA */
484 tutc = tutc & ~TC_SAC;                                  /* clear addr change */
485 
486 switch (fnc) {                                          /* case on function */
487 
488     case FNC_FCLR:                                      /* drive clear */
489         tuer = 0;                                       /* clear errors */
490         tutc = tutc & ~TC_FCS;                          /* clear fc status */
491         tufs = tufs & ~(FS_SAT | FS_SSC | FS_ID | FS_ERR);
492         sim_cancel (uptr);                              /* reset drive */
493         uptr->USTAT = 0;
494     case FNC_NOP:
495         tucs1 = tucs1 & ~CS1_GO;                        /* no operation */
496         return SCPE_OK;
497 
498     case FNC_RIP:                                       /* read-in preset */
499         tutc = TC_RIP;                                  /* set tutc */
500         sim_tape_rewind (&tu_unit[0]);                  /* rewind unit 0 */
501         tu_unit[0].USTAT = 0;
502         tucs1 = tucs1 & ~CS1_GO;
503         tufs = tufs & ~FS_TMK;
504         return SCPE_OK;
505 
506     case FNC_UNLOAD:                                    /* unload */
507         if ((uptr->flags & UNIT_ATT) == 0) {            /* unattached? */
508             tu_set_er (ER_UNS);
509             break;
510             }
511         detach_unit (uptr);
512         uptr->USTAT = FS_REW;
513         sim_activate (uptr, tu_time);
514         tucs1 = tucs1 & ~CS1_GO;
515         tufs = tufs & ~FS_TMK;
516         return SCPE_OK;
517 
518     case FNC_REWIND:
519         if ((uptr->flags & UNIT_ATT) == 0) {            /* unattached? */
520             tu_set_er (ER_UNS);
521             break;
522             }
523         uptr->USTAT = FS_PIP | FS_REW;
524         sim_activate (uptr, tu_time);
525         tucs1 = tucs1 & ~CS1_GO;
526         tufs = tufs & ~FS_TMK;
527         return SCPE_OK;
528 
529     case FNC_SPACEF:
530         if ((uptr->flags & UNIT_ATT) == 0) {            /* unattached? */
531             tu_set_er (ER_UNS);
532             break;
533             }
534         if (sim_tape_eot (uptr) || ((tutc & TC_FCS) == 0)) {
535             tu_set_er (ER_NXF);
536             break;
537             }
538         uptr->USTAT = FS_PIP;
539         goto GO_XFER;
540 
541     case FNC_SPACER:
542         if ((uptr->flags & UNIT_ATT) == 0) {            /* unattached? */
543             tu_set_er (ER_UNS);
544             break;
545             }
546         if (sim_tape_bot (uptr) || ((tutc & TC_FCS) == 0)) {
547             tu_set_er (ER_NXF);
548             break;
549             }
550         uptr->USTAT = FS_PIP;
551         goto GO_XFER;
552 
553     case FNC_WCHKR:                                     /* wchk = read */
554     case FNC_READR:                                     /* read rev */
555         if (tufs & FS_BOT) {                            /* beginning of tape? */
556             tu_set_er (ER_NXF);
557             break;
558             }
559         goto DATA_XFER;
560 
561     case FNC_WRITE:                                     /* write */
562         if (((tutc & TC_FCS) == 0) ||                   /* frame cnt = 0? */
563             ((den == TC_800) && (tufc > 0777765))) {    /* NRZI, fc < 13? */
564             tu_set_er (ER_NXF);
565             break;
566             }
567     case FNC_WREOF:                                     /* write tape mark */
568     case FNC_ERASE:                                     /* erase */
569         if (sim_tape_wrp (uptr)) {                      /* write locked? */
570             tu_set_er (ER_NXF);
571             break;
572             }
573     case FNC_WCHKF:                                     /* wchk = read */
574     case FNC_READF:                                     /* read */
575     DATA_XFER:
576         if ((uptr->flags & UNIT_ATT) == 0) {            /* unattached? */
577             tu_set_er (ER_UNS);
578             break;
579             }
580         if (fmt_test[GET_FMT (tutc)] == 0) {            /* invalid format? */
581             tu_set_er (ER_FER);
582             break;
583             }
584         if (uptr->UDENS == UD_UNK)                      /* set dens */
585             uptr->UDENS = den;
586         uptr->USTAT = 0;
587     GO_XFER:
588         tufs = tufs & ~(FS_TMK | FS_ID);                /* clear eof, id */
589         sim_activate (uptr, tu_time);
590         return SCPE_OK;
591 
592     default:                                            /* all others */
593         tu_set_er (ER_ILF);                             /* not supported */
594         break;
595         }                                               /* end case function */
596 
597 tucs1 = tucs1 & ~CS1_GO;                                /* clear go */
598 tu_update_fs (FS_ATA, drv);                             /* set attn */
599 return MBE_GOE;
600 }
601 
602 /* Abort transfer */
603 
tu_abort(void)604 int32 tu_abort (void)
605 {
606 return tu_reset (&tu_dev);
607 }
608 
609 /* Unit service
610 
611    Complete movement or data transfer command
612    Unit must exist - can't remove an active unit
613    Unit must be attached - detach cancels in progress operations
614 */
615 
tu_svc(UNIT * uptr)616 t_stat tu_svc (UNIT *uptr)
617 {
618 int32 fnc, fmt, j, xbc;
619 int32 fc, drv;
620 t_mtrlnt i, tbc;
621 t_stat st, r = SCPE_OK;
622 
623 drv = (int32) (uptr - tu_dev.units);                    /* get drive # */
624 if (uptr->USTAT & FS_REW) {                             /* rewind or unload? */
625     sim_tape_rewind (uptr);                             /* rewind tape */
626     uptr->USTAT = 0;                                    /* clear status */
627     tu_update_fs (FS_ATA | FS_SSC, drv);
628     return SCPE_OK;
629     }
630 
631 fnc = GET_FNC (tucs1);                                  /* get command */
632 fmt = GET_FMT (tutc);                                   /* get format */
633 fc = 0200000 - tufc;                                    /* get frame count */
634 uptr->USTAT = 0;                                        /* clear status */
635 
636 if ((uptr->flags & UNIT_ATT) == 0) {
637     tu_set_er (ER_UNS);                                 /* set formatter error */
638     if (fnc >= FNC_XFER)
639         mba_set_don (tu_dib.ba);
640     tu_update_fs (FS_ATA, drv);
641     return (tu_stopioe? SCPE_UNATT: SCPE_OK);
642     }
643 switch (fnc) {                                          /* case on function */
644 
645 /* Non-data transfer commands - set ATA when done */
646 
647     case FNC_SPACEF:                                    /* space forward */
648         do {
649             tufc = (tufc + 1) & 0177777;                /* incr fc */
650             if ((st = sim_tape_sprecf (uptr, &tbc))) {  /* space rec fwd, err? */
651                 r = tu_map_err (drv, st, 0);            /* map error */
652                 break;
653                 }
654             } while ((tufc != 0) && !sim_tape_eot (uptr));
655         if (tufc)
656             tu_set_er (ER_FCE);
657         else tutc = tutc & ~TC_FCS;
658         break;
659 
660     case FNC_SPACER:                                    /* space reverse */
661         do {
662             tufc = (tufc + 1) & 0177777;                /* incr wc */
663             if ((st = sim_tape_sprecr (uptr, &tbc))) {  /* space rec rev, err? */
664                 r = tu_map_err (drv, st, 0);            /* map error */
665                 break;
666                 }
667             } while (tufc != 0);
668         if (tufc)
669             tu_set_er (ER_FCE);
670         else tutc = tutc & ~TC_FCS;
671         break;
672 
673     case FNC_WREOF:                                     /* write end of file */
674         if ((st = sim_tape_wrtmk (uptr)))               /* write tmk, err? */
675             r = tu_map_err (drv, st, 0);                /* map error */
676         break;
677 
678     case FNC_ERASE:
679         if (sim_tape_wrp (uptr))                        /* write protected? */
680             r = tu_map_err (drv, MTSE_WRP, 0);          /* map error */
681         break;
682 
683 /* Unit service - data transfer commands */
684 
685     case FNC_READF:                                     /* read */
686     case FNC_WCHKF:                                     /* wcheck = read */
687         tufc = 0;                                       /* clear frame count */
688         if ((uptr->UDENS == TC_1600) && sim_tape_bot (uptr))
689             tufs = tufs | FS_ID;                        /* PE BOT? ID burst */
690         if ((st = sim_tape_rdrecf (uptr, xbuf, &tbc, MT_MAXFR))) { /* read fwd */
691             if (st == MTSE_TMK)                         /* tmk also sets FCE */
692                 tu_set_er (ER_FCE);
693             r = tu_map_err (drv, st, 1);                /* map error */
694             break;                                      /* done */
695             }
696         for (i = tbc; i < tbc + 4; i++)                 /* pad with 0's */
697             xbuf[i] = 0;
698         if (fmt == TC_CDUMP) {                          /* core dump? */
699             for (i = j = 0; i < tbc; i = i + 4) {
700                 wbuf[j++] = ((uint16) xbuf[i] & 0xF) |
701                     (((uint16) (xbuf[i + 1] & 0xF)) << 4) |
702                     (((uint16) (xbuf[i + 2] & 0xF)) << 8) |
703                     (((uint16) (xbuf[i + 3] & 0xf)) << 12);
704                 }
705             xbc = (tbc + 1) >> 1;
706             }
707         else {                                          /* standard */
708             for (i = j = 0; i < tbc; i = i + 2) {
709                 wbuf[j++] = ((uint16) xbuf[i]) |
710                     (((uint16) xbuf[i + 1]) << 8);
711                 }
712             xbc = tbc;
713             }
714         if (mba_get_bc (tu_dib.ba) > xbc)               /* record short? */
715             tu_set_er (ER_FCE);                         /* set FCE, ATN */
716         if (fnc == FNC_WCHKF)
717             mba_chbufW (tu_dib.ba, xbc, wbuf);
718         else mba_wrbufW (tu_dib.ba, xbc, wbuf);
719         tufc = tbc & 0177777;
720         break;
721 
722     case FNC_WRITE:                                     /* write */
723         xbc = mba_rdbufW (tu_dib.ba, fc, wbuf);         /* read buffer */
724         if (xbc == 0)                                   /* anything?? */
725             break;
726         if (fmt == TC_CDUMP) {                          /* core dump? */
727             for (i = j = 0; j < xbc; j = j + 1) {
728                 xbuf[i++] = wbuf[j] & 0xF;
729                 xbuf[i++] = (wbuf[j] >> 4) & 0xF;
730                 xbuf[i++] = (wbuf[j] >> 8) & 0xF;
731                 xbuf[i++] = (wbuf[j] >> 12) & 0xF;
732                 }
733             tbc = (xbc + 1) >> 1;
734             }
735         else {                                          /* standard */
736             for (i = j = 0; j < xbc; j = j + 1) {
737                 xbuf[i++] = wbuf[j] & 0377;
738                 xbuf[i++] = (wbuf[j] >> 8) & 0377;
739 				}
740             tbc = xbc;
741             }
742         if ((st = sim_tape_wrrecf (uptr, xbuf, tbc)))   /* write rec, err? */
743             r = tu_map_err (drv, st, 1);                /* map error */
744         else {
745             tufc = (tufc + tbc) & 0177777;
746             if (tufc == 0)
747                 tutc = tutc & ~TC_FCS;
748             }
749         break;
750 
751     case FNC_READR:                                     /* read reverse */
752     case FNC_WCHKR:                                     /* wcheck = read */
753         tufc = 0;                                       /* clear frame count */
754         if ((st = sim_tape_rdrecr (uptr, xbuf + 4, &tbc, MT_MAXFR))) { /* read rev */
755             if (st == MTSE_TMK)                         /* tmk also sets FCE */
756                 tu_set_er (ER_FCE);
757             r = tu_map_err (drv, st, 1);                /* map error */
758             break;                                      /* done */
759             }
760         for (i = 0; i < 4; i++) xbuf[i] = 0;            /* pad with 0's */
761         if (fmt == TC_CDUMP) {                          /* core dump? */
762             for (i = tbc + 3, j = 0; i > 3; i = i - 4) {
763                 wbuf[j++] = ((uint16) xbuf[i] & 0xF) |
764                     (((uint16) (xbuf[i - 1] & 0xF)) << 4) |
765                     (((uint16) (xbuf[i - 2] & 0xF)) << 8) |
766                     (((uint16) (xbuf[i - 3] & 0xf)) << 12);
767                 }
768             xbc = (tbc + 1) >> 1;
769             }
770         else {                                          /* standard */
771             for (i = tbc + 3, j = 0; i > 3; i = i - 2) {
772                 wbuf[j++] = ((uint16) xbuf[i]) |
773                     (((uint16) xbuf[i - 1]) << 8);
774                 }
775             xbc = tbc;
776             }
777         if (mba_get_bc (tu_dib.ba) > xbc)               /* record short? */
778             tu_set_er (ER_FCE);                         /* set FCE, ATN */
779         if (fnc == FNC_WCHKR)
780             mba_chbufW (tu_dib.ba, xbc, wbuf);
781         else mba_wrbufW (tu_dib.ba, xbc, wbuf);
782         tufc = tbc & 0177777;
783         break;
784         }                                               /* end case */
785 
786 tucs1 = tucs1 & ~CS1_GO;                                /* clear go */
787 if (fnc >= FNC_XFER) {                                  /* data xfer? */
788     mba_set_don (tu_dib.ba);                            /* set done */
789     tu_update_fs (0, drv);                              /* update fs */
790     }
791 else tu_update_fs (FS_ATA, drv);                        /* no, set attn */
792 if (DEBUG_PRS (tu_dev)) {
793     fprintf (sim_deb, ">>TU%d DONE: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=",
794              drv, tu_fname[fnc], tufc, tufs, tuer);
795     fprint_val (sim_deb, uptr->pos, 10, T_ADDR_W, PV_LEFT);
796     fprintf (sim_deb, "\n");
797     }
798 return SCPE_OK;
799 }
800 
801 /* Set formatter error */
802 
tu_set_er(int32 flg)803 void tu_set_er (int32 flg)
804 {
805 tuer = tuer | flg;
806 tufs = tufs | FS_ATA;
807 mba_upd_ata (tu_dib.ba, 1);
808 return;
809 }
810 
811 /* Clear attention */
812 
tu_clr_as(int32 mask)813 void tu_clr_as (int32 mask)
814 {
815 if (mask & AS_U0)
816     tufs = tufs & ~FS_ATA;
817 mba_upd_ata (tu_dib.ba, tufs & FS_ATA);
818 return;
819 }
820 
821 /* Formatter update status */
822 
tu_update_fs(int32 flg,int32 drv)823 void tu_update_fs (int32 flg, int32 drv)
824 {
825 int32 act = sim_is_active (&tu_unit[drv]);
826 
827 tufs = (tufs & ~FS_DYN) | FS_FPR | flg;
828 if (tu_unit[drv].flags & UNIT_ATT) {
829     tufs = tufs | FS_MOL | tu_unit[drv].USTAT;
830     if (tu_unit[drv].UDENS == TC_1600)
831         tufs = tufs | FS_PE;
832     if (sim_tape_wrp (&tu_unit[drv]))
833         tufs = tufs | FS_WRL;
834     if (!act) {
835         if (sim_tape_bot (&tu_unit[drv]))
836             tufs = tufs | FS_BOT;
837         if (sim_tape_eot (&tu_unit[drv]))
838             tufs = tufs | FS_EOT;
839         }
840     }
841 if (tuer)
842     tufs = tufs | FS_ERR;
843 if (tufs && !act)
844     tufs = tufs | FS_RDY;
845 if (flg & FS_ATA)
846     mba_upd_ata (tu_dib.ba, 1);
847 return;
848 }
849 
850 /* Map tape error status */
851 
tu_map_err(int32 drv,t_stat st,t_bool qdt)852 t_stat tu_map_err (int32 drv, t_stat st, t_bool qdt)
853 {
854 switch (st) {
855 
856     case MTSE_FMT:                                      /* illegal fmt */
857     case MTSE_UNATT:                                    /* not attached */
858         tu_set_er (ER_NXF);                             /* can't execute */
859         if (qdt)                                        /* set exception */
860             mba_set_exc (tu_dib.ba);
861         break;
862 
863     case MTSE_TMK:                                      /* end of file */
864         tufs = tufs | FS_TMK;
865         break;
866 
867     case MTSE_IOERR:                                    /* IO error */
868         tu_set_er (ER_VPE);                             /* flag error */
869         if (qdt)                                        /* set exception */
870             mba_set_exc (tu_dib.ba);
871         return (tu_stopioe? SCPE_IOERR: SCPE_OK);
872 
873     case MTSE_INVRL:                                    /* invalid rec lnt */
874         tu_set_er (ER_VPE);                             /* flag error */
875         if (qdt)                                        /* set exception */
876             mba_set_exc (tu_dib.ba);
877         return SCPE_MTRLNT;
878 
879     case MTSE_RECE:                                     /* record in error */
880         tu_set_er (ER_CRC);                             /* set crc err */
881         if (qdt)                                        /* set exception */
882             mba_set_exc (tu_dib.ba);
883         break;
884 
885     case MTSE_EOM:                                      /* end of medium */
886         tu_set_er (ER_OPI);                             /* incomplete */
887         if (qdt)                                        /* set exception */
888             mba_set_exc (tu_dib.ba);
889         break;
890 
891     case MTSE_BOT:                                      /* reverse into BOT */
892         return SCPE_OK;
893 
894     case MTSE_WRP:                                      /* write protect */
895         tu_set_er (ER_NXF);                             /* can't execute */
896         if (qdt)                                        /* set exception */
897             mba_set_exc (tu_dib.ba);
898         break;
899 
900     default:                                            /* unknown error */
901         return SCPE_IERR;
902         }
903 
904 return SCPE_OK;
905 }
906 
907 /* Reset routine */
908 
tu_reset(DEVICE * dptr)909 t_stat tu_reset (DEVICE *dptr)
910 {
911 int32 u;
912 UNIT *uptr;
913 
914 mba_set_enbdis (MBA_TU, tu_dev.flags & DEV_DIS);
915 tucs1 = 0;
916 tufc = 0;
917 tuer = 0;
918 tufs = FS_FPR | FS_RDY;
919 if (sim_switches & SWMASK ('P'))                        /* powerup? clr TC */
920     tutc = 0;
921 else tutc = tutc & ~TC_FCS;                             /* no, clr <fcs> */
922 for (u = 0; u < TU_NUMDR; u++) {                        /* loop thru units */
923     uptr = tu_dev.units + u;
924     sim_tape_reset (uptr);                              /* clear pos flag */
925     sim_cancel (uptr);                                  /* cancel activity */
926     uptr->USTAT = 0;
927     }
928 if (xbuf == NULL)
929     xbuf = (uint8 *) calloc (MT_MAXFR + 4, sizeof (uint8));
930 if (xbuf == NULL)
931     return SCPE_MEM;
932 if (wbuf == NULL)
933     wbuf = (uint16 *) calloc ((MT_MAXFR + 4) >> 1, sizeof (uint16));
934 if (wbuf == NULL)
935     return SCPE_MEM;
936 return SCPE_OK;
937 }
938 
939 /* Attach routine */
940 
tu_attach(UNIT * uptr,char * cptr)941 t_stat tu_attach (UNIT *uptr, char *cptr)
942 {
943 int32 drv = uptr - tu_dev.units, flg;
944 t_stat r;
945 
946 r = sim_tape_attach (uptr, cptr);
947 if (r != SCPE_OK)
948     return r;
949 uptr->USTAT = 0;                                        /* clear unit status */
950 uptr->UDENS = UD_UNK;                                   /* unknown density */
951 flg = FS_ATA | FS_SSC;                                  /* set attention */
952 if (GET_DRV (tutc) == drv)                              /* sel drv? set SAT */
953     flg = flg | FS_SAT;
954 tu_update_fs (flg, drv);                                /* update status */
955 return r;
956 }
957 
958 /* Detach routine */
959 
tu_detach(UNIT * uptr)960 t_stat tu_detach (UNIT* uptr)
961 {
962 int32 drv = uptr - tu_dev.units;
963 
964 if (!(uptr->flags & UNIT_ATT))                          /* attached? */
965     return SCPE_OK;
966 uptr->USTAT = 0;                                        /* clear status flags */
967 tu_update_fs (FS_ATA | FS_SSC, drv);                    /* update status */
968 return sim_tape_detach (uptr);
969 }
970 
971 /* Set/show formatter type */
972 
tu_set_fmtr(UNIT * uptr,int32 val,char * cptr,void * desc)973 t_stat tu_set_fmtr (UNIT *uptr, int32 val, char *cptr, void *desc)
974 {
975 DEVICE *dptr = find_dev_from_unit (uptr);
976 
977 if (cptr != NULL)
978     return SCPE_ARG;
979 if (dptr == NULL)
980     return SCPE_IERR;
981 if (val)
982     dptr->flags = dptr->flags | DEV_TM03;
983 else dptr->flags = dptr->flags & ~DEV_TM03;
984 return SCPE_OK;
985 }
986 
tu_show_fmtr(FILE * st,UNIT * uptr,int32 val,void * desc)987 t_stat tu_show_fmtr (FILE *st, UNIT *uptr, int32 val, void *desc)
988 {
989 DEVICE *dptr = find_dev_from_unit (uptr);
990 
991 if (dptr == NULL)
992     return SCPE_IERR;
993 fprintf (st, "TM0%d", (dptr->flags & DEV_TM03? 3: 2));
994 return SCPE_OK;
995 }
996 
997 /* Device bootstrap */
998 
999 #if defined (PDP11)
1000 
1001 #elif defined (VM_PDP11)
1002 
1003 #define BOOT_START      016000                          /* start */
1004 #define BOOT_ENTRY      (BOOT_START + 002)              /* entry */
1005 #define BOOT_UNIT       (BOOT_START + 010)              /* unit number */
1006 #define BOOT_CSR        (BOOT_START + 014)              /* CSR */
1007 #define BOOT_LEN        (sizeof (boot_rom) / sizeof (uint16))
1008 
1009 static const uint16 boot_rom[] = {
1010     0046515,                        /* "MM" */
1011     0012706, BOOT_START,            /* mov #boot_start, sp */
1012     0012700, 0000000,               /* mov #unit, r0 */
1013     0012701, 0172440,               /* mov #TUCS1, r1 */
1014     0012761, 0000040, 0000010,      /* mov #CS2_CLR, 10(r1) ; reset */
1015     0012711, 0000021,               /* mov #RIP+GO, (r1)    ; rip */
1016     0010004,                        /* mov r0, r4 */
1017     0052704, 0002300,               /* bis #2300, r4        ; set den */
1018     0010461, 0000032,               /* mov r4, 32(r1)       ; set unit */
1019     0012761, 0177777, 0000006,      /* mov #-1, 6(r1)       ; set fc */
1020     0012711, 0000031,               /* mov #SPCF+GO, (r1)   ; skip rec */
1021     0105761, 0000012,               /* tstb 12 (r1)         ; fmtr rdy? */
1022     0100375,                        /* bpl .-4 */
1023     0012761, 0177000, 0000002,      /* mov #-1000, 2(r1)    ; set wc */
1024     0005061, 0000004,               /* clr 4(r1)            ; clr ba */
1025     0005061, 0000006,               /* clr 6(r1)            ; clr fc */
1026     0012711, 0000071,               /* mov #READ+GO, (r1)   ; read  */
1027     0105711,                        /* tstb (r1)            ; wait */
1028     0100376,                        /* bpl .-2 */
1029     0005002,                        /* clr R2 */
1030     0005003,                        /* clr R3 */
1031     0012704, BOOT_START+020,        /* mov #start+020, r4 */
1032     0005005,                        /* clr R5 */
1033     0105011,                        /* clrb (r1) */
1034     0005007                         /* clr PC */
1035     };
1036 
tu_boot(int32 unitno,DEVICE * dptr)1037 t_stat tu_boot (int32 unitno, DEVICE *dptr)
1038 {
1039 int32 i;
1040 extern int32 saved_PC;
1041 extern uint16 *M;
1042 
1043 for (i = 0; i < BOOT_LEN; i++)
1044     M[(BOOT_START >> 1) + i] = boot_rom[i];
1045 M[BOOT_UNIT >> 1] = unitno & (TU_NUMDR - 1);
1046 M[BOOT_CSR >> 1] = mba_get_csr (tu_dib.ba) & DMASK;
1047 saved_PC = BOOT_ENTRY;
1048 return SCPE_OK;
1049 }
1050 
1051 #else
1052 
tu_boot(int32 unitno,DEVICE * dptr)1053 t_stat tu_boot (int32 unitno, DEVICE *dptr)
1054 {
1055 return SCPE_NOFNC;
1056 }
1057 
1058 #endif
1059