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