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