First Commit of my working state
[simh.git] / PDP11 / pdp11_ta.c
1 /* pdp11_ta.c: PDP-11 cassette tape simulator
2
3 Copyright (c) 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 ATAION OF CONTRATA, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNETAION 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 ta TA11/TU60 cassette tape
27
28 06-Aug-07 RMS Foward op at BOT skips initial file gap
29
30 Magnetic tapes are represented as a series of variable records
31 of the form:
32
33 32b byte count
34 byte 0
35 byte 1
36 :
37 byte n-2
38 byte n-1
39 32b byte count
40
41 If the byte count is odd, the record is padded with an extra byte
42 of junk. File marks are represented by a byte count of 0.
43
44 Cassette format differs in one very significant way: it has file gaps
45 rather than file marks. If the controller spaces or reads into a file
46 gap and then reverses direction, the file gap is not seen again. This
47 is in contrast to magnetic tapes, where the file mark is a character
48 sequence and is seen again if direction is reversed. In addition,
49 cassettes have an initial file gap which is automatically skipped on
50 forward operations from beginning of tape.
51 */
52
53 #include "pdp11_defs.h"
54 #include "sim_tape.h"
55
56 #define TA_NUMDR 2 /* #drives */
57 #define FNC u3 /* unit function */
58 #define UST u4 /* unit status */
59 #define TA_SIZE 93000 /* chars/tape */
60 #define TA_MAXFR (TA_SIZE) /* max record lnt */
61
62 /* Control/status - TACS */
63
64 #define TACS_ERR (1 << CSR_V_ERR) /* error */
65 #define TACS_CRC 0040000 /* CRC */
66 #define TACS_BEOT 0020000 /* BOT/EOT */
67 #define TACS_WLK 0010000 /* write lock */
68 #define TACS_EOF 0004000 /* end file */
69 #define TACS_TIM 0002000 /* timing */
70 #define TACS_EMP 0001000 /* empty */
71 #define TACS_V_UNIT 8 /* unit */
72 #define TACS_M_UNIT (TA_NUMDR - 1)
73 #define TACS_UNIT (TACS_M_UNIT << TACS_V_UNIT)
74 #define TACS_TR (1 << CSR_V_DONE) /* transfer req */
75 #define TACS_IE (1 << CSR_V_IE) /* interrupt enable */
76 #define TACS_RDY 0000040 /* ready */
77 #define TACS_ILBS 0000020 /* start CRC */
78 #define TACS_V_FNC 1 /* function */
79 #define TACS_M_FNC 07
80 #define TACS_WFG 00
81 #define TACS_WRITE 01
82 #define TACS_READ 02
83 #define TACS_SRF 03
84 #define TACS_SRB 04
85 #define TACS_SFF 05
86 #define TACS_SFB 06
87 #define TACS_REW 07
88 #define TACS_2ND 010
89 #define TACS_3RD 030
90 #define TACS_FNC (TACS_M_FNC << TACS_V_FNC)
91 #define TACS_GO (1 << CSR_V_GO) /* go */
92 #define TACS_W (TACS_UNIT|TACS_IE|TACS_ILBS|TACS_FNC)
93 #define TACS_XFRERR (TACS_ERR|TACS_CRC|TACS_WLK|TACS_EOF|TACS_TIM)
94 #define GET_UNIT(x) (((x) >> TACS_V_UNIT) & TACS_M_UNIT)
95 #define GET_FNC(x) (((x) >> TACS_V_FNC) & TACS_M_FNC)
96
97 /* Function code flags */
98
99 #define OP_WRI 01 /* op is a write */
100 #define OP_REV 02 /* op is rev motion */
101 #define OP_FWD 04 /* op is fwd motion */
102
103 /* Unit status flags */
104
105 #define UST_REV (OP_REV) /* last op was rev */
106 #define UST_GAP 01 /* last op hit gap */
107
108 extern int32 int_req[IPL_HLVL];
109 extern FILE *sim_deb;
110
111 uint32 ta_cs = 0; /* control/status */
112 uint32 ta_idb = 0; /* input data buf */
113 uint32 ta_odb = 0; /* output data buf */
114 uint32 ta_write = 0; /* TU60 write flag */
115 uint32 ta_bptr = 0; /* buf ptr */
116 uint32 ta_blnt = 0; /* buf length */
117 int32 ta_stime = 1000; /* start time */
118 int32 ta_ctime = 100; /* char latency */
119 uint32 ta_stopioe = 1; /* stop on error */
120 uint8 *ta_xb = NULL; /* transfer buffer */
121 static uint8 ta_fnc_tab[TACS_M_FNC + 1] = {
122 OP_WRI|OP_FWD, OP_WRI|OP_FWD, OP_FWD, OP_REV,
123 OP_REV , OP_FWD, OP_FWD, 0
124 };
125
126 DEVICE ta_dev;
127 t_stat ta_rd (int32 *data, int32 PA, int32 access);
128 t_stat ta_wr (int32 data, int32 PA, int32 access);
129 t_stat ta_svc (UNIT *uptr);
130 t_stat ta_reset (DEVICE *dptr);
131 t_stat ta_attach (UNIT *uptr, char *cptr);
132 t_stat ta_detach (UNIT *uptr);
133 void ta_go (void);
134 t_stat ta_map_err (UNIT *uptr, t_stat st);
135 UNIT *ta_busy (void);
136 void ta_set_tr (void);
137 uint32 ta_updsta (UNIT *uptr);
138 uint32 ta_crc (uint8 *buf, uint32 cnt);
139
140 /* TA data structures
141
142 ta_dev TA device descriptor
143 ta_unit TA unit list
144 ta_reg TA register list
145 ta_mod TA modifier list
146 */
147
148 DIB ta_dib = {
149 IOBA_TA, IOLN_TA, &ta_rd, &ta_wr,
150 1, IVCL (TA), VEC_TA, { NULL }
151 };
152
153 UNIT ta_unit[] = {
154 { UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) },
155 { UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) },
156 };
157
158 REG ta_reg[] = {
159 { ORDATA (TACS, ta_cs, 16) },
160 { ORDATA (TAIDB, ta_idb, 8) },
161 { ORDATA (TAODB, ta_odb, 8) },
162 { FLDATA (WRITE, ta_write, 0) },
163 { FLDATA (INT, IREQ (TA), INT_V_TA) },
164 { FLDATA (ERR, ta_cs, CSR_V_ERR) },
165 { FLDATA (TR, ta_cs, CSR_V_DONE) },
166 { FLDATA (IE, ta_cs, CSR_V_IE) },
167 { DRDATA (BPTR, ta_bptr, 17) },
168 { DRDATA (BLNT, ta_blnt, 17) },
169 { DRDATA (STIME, ta_stime, 24), PV_LEFT + REG_NZ },
170 { DRDATA (CTIME, ta_ctime, 24), PV_LEFT + REG_NZ },
171 { FLDATA (STOP_IOE, ta_stopioe, 0) },
172 { URDATA (UFNC, ta_unit[0].FNC, 8, 5, 0, TA_NUMDR, 0), REG_HRO },
173 { URDATA (UST, ta_unit[0].UST, 8, 2, 0, TA_NUMDR, 0), REG_HRO },
174 { URDATA (POS, ta_unit[0].pos, 10, T_ADDR_W, 0,
175 TA_NUMDR, PV_LEFT | REG_RO) },
176 { ORDATA (DEVADDR, ta_dib.ba, 32), REG_HRO },
177 { ORDATA (DEVVEC, ta_dib.vec, 16), REG_HRO },
178 { NULL }
179 };
180
181 MTAB ta_mod[] = {
182 { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },
183 { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },
184 // { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
185 // &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
186 { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL,
187 NULL, &sim_tape_show_capac, NULL },
188 { MTAB_XTD|MTAB_VDV, IOLN_TA, "ADDRESS", "ADDRESS",
189 &set_addr, &show_addr, NULL },
190 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
191 &set_vec, &show_vec, NULL },
192 { 0 }
193 };
194
195 DEVICE ta_dev = {
196 "TA", ta_unit, ta_reg, ta_mod,
197 TA_NUMDR, 10, 31, 1, 8, 8,
198 NULL, NULL, &ta_reset,
199 NULL, &ta_attach, &ta_detach,
200 &ta_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG
201 };
202
203 /* I/O dispatch routines, I/O addresses 17777500 - 17777503
204
205 17777500 TACS read/write
206 17777502 TADB read/write
207 */
208
209 t_stat ta_rd (int32 *data, int32 PA, int32 access)
210 {
211 switch ((PA >> 1) & 01) { /* decode PA<1> */
212
213 case 0: /* TACSR */
214 *data = ta_updsta (NULL); /* update status */
215 break;
216
217 case 1: /* TADB */
218 *data = ta_idb; /* return byte */
219 ta_cs &= ~TACS_TR; /* clear tra req */
220 ta_updsta (NULL);
221 break;
222 }
223
224 return SCPE_OK;
225 }
226
227 t_stat ta_wr (int32 data, int32 PA, int32 access)
228 {
229 switch ((PA >> 1) & 01) { /* decode PA<1> */
230
231 case 0: /* TACS */
232 if (access == WRITEB) data = (PA & 1)? /* byte write? */
233 (ta_cs & 0377) | (data << 8): /* merge old */
234 (ta_cs & ~0377) | data;
235 ta_cs = (ta_cs & ~TACS_W) | (data & TACS_W); /* merge new */
236 if ((data & CSR_GO) && !ta_busy ()) /* go, not busy? */
237 ta_go (); /* start operation */
238 if (ta_cs & TACS_ILBS) ta_cs &= ~TACS_TR; /* ILBS inhibits TR */
239 break;
240
241 case 1: /* TADB */
242 if (PA & 1) break; /* ignore odd byte */
243 ta_odb = data; /* return byte */
244 ta_cs &= ~TACS_TR; /* clear tra req */
245 break;
246 } /* end switch */
247
248 ta_updsta (NULL); /* update status */
249 return SCPE_OK;
250 }
251
252 /* Start a new operation - cassette is not busy */
253
254 void ta_go (void)
255 {
256 UNIT *uptr = ta_dev.units + GET_UNIT (ta_cs);
257 uint32 fnc = GET_FNC (ta_cs);
258 uint32 flg = ta_fnc_tab[fnc];
259 uint32 old_ust = uptr->UST;
260
261 if (DEBUG_PRS (ta_dev)) fprintf (sim_deb,
262 ">>TA start: op=%o, old_sta = %o, pos=%d\n",
263 fnc, uptr->UST, uptr->pos);
264 ta_cs &= ~(TACS_XFRERR|TACS_EMP|TACS_TR|TACS_RDY); /* clr err, tr, rdy */
265 ta_bptr = 0; /* init buffer */
266 ta_blnt = 0;
267 if ((uptr->flags & UNIT_ATT) == 0) {
268 ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY;
269 return;
270 }
271 if (flg & OP_WRI) { /* write op? */
272 if (sim_tape_wrp (uptr)) { /* locked? */
273 ta_cs |= TACS_ERR|TACS_WLK|TACS_RDY; /* don't start */
274 return;
275 }
276 ta_odb = 0;
277 ta_write = 1;
278 }
279 else {
280 ta_idb = 0;
281 ta_write = 0;
282 }
283 ta_cs &= ~TACS_BEOT; /* tape in motion */
284 uptr->FNC = fnc; /* save function */
285 if ((fnc != TACS_REW) && !(flg & OP_WRI)) { /* spc/read cmd? */
286 t_mtrlnt t;
287 t_stat st;
288 uptr->UST = flg & UST_REV; /* save direction */
289 if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */
290 st = sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR); /* skip file gap */
291 if (st != MTSE_TMK) /* not there? */
292 sim_tape_rewind (uptr); /* restore tap pos */
293 else old_ust = 0; /* defang next */
294 }
295 if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* reverse in gap? */
296 if (uptr->UST) /* skip file gap */
297 sim_tape_rdrecr (uptr, ta_xb, &t, TA_MAXFR);
298 else sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR);
299 if (DEBUG_PRS (ta_dev)) fprintf (sim_deb,
300 ">>TA skip gap: op=%o, old_sta = %o, pos=%d\n",
301 fnc, uptr->UST, uptr->pos);
302 }
303 }
304 else uptr->UST = 0;
305 sim_activate (uptr, ta_stime); /* schedule op */
306 return;
307 }
308
309 /* Unit service */
310
311 t_stat ta_svc (UNIT *uptr)
312 {
313 uint32 i, crc;
314 uint32 flg = ta_fnc_tab[uptr->FNC & TACS_M_FNC];
315 t_mtrlnt tbc;
316 t_stat st, r;
317
318 if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
319 ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY;
320 ta_updsta (uptr); /* update status */
321 return (ta_stopioe? SCPE_UNATT: SCPE_OK);
322 }
323 if (((flg & OP_FWD) && sim_tape_eot (uptr)) || /* illegal motion? */
324 ((flg & OP_REV) && sim_tape_bot (uptr))) {
325 ta_cs |= TACS_ERR|TACS_BEOT|TACS_RDY; /* error */
326 ta_updsta (uptr);
327 return SCPE_OK;
328 }
329
330 r = SCPE_OK;
331 switch (uptr->FNC) { /* case on function */
332
333 case TACS_READ: /* read start */
334 st = sim_tape_rdrecf (uptr, ta_xb, &ta_blnt, TA_MAXFR); /* get rec */
335 if (st == MTSE_RECE) ta_cs |= TACS_ERR|TACS_CRC; /* rec in err? */
336 else if (st != MTSE_OK) { /* other error? */
337 r = ta_map_err (uptr, st); /* map error */
338 break;
339 }
340 crc = ta_crc (ta_xb, ta_blnt); /* calculate CRC */
341 ta_xb[ta_blnt++] = (crc >> 8) & 0377; /* append to buffer */
342 ta_xb[ta_blnt++] = crc & 0377;
343 uptr->FNC |= TACS_2ND; /* next state */
344 sim_activate (uptr, ta_ctime); /* sched next char */
345 return SCPE_OK;
346
347 case TACS_READ|TACS_2ND: /* read char */
348 if (ta_bptr < ta_blnt) /* more chars? */
349 ta_idb = ta_xb[ta_bptr++];
350 else { /* no */
351 ta_idb = 0;
352 ta_cs |= TACS_ERR|TACS_CRC; /* overrun */
353 break; /* tape stops */
354 }
355 if (ta_cs & TACS_ILBS) { /* CRC seq? */
356 uptr->FNC |= TACS_3RD; /* next state */
357 sim_activate (uptr, ta_stime); /* sched CRC chk */
358 }
359 else {
360 ta_set_tr (); /* set tra req */
361 sim_activate (uptr, ta_ctime); /* sched next char */
362 }
363 return SCPE_OK;
364
365 case TACS_READ|TACS_3RD: /* second read CRC */
366 if (ta_bptr != ta_blnt) { /* partial read? */
367 crc = ta_crc (ta_xb, ta_bptr + 2); /* actual CRC */
368 if (crc != 0) ta_cs |= TACS_ERR|TACS_CRC; /* must be zero */
369 }
370 break; /* read done */
371
372 case TACS_WRITE: /* write start */
373 for (i = 0; i < TA_MAXFR; i++) ta_xb[i] = 0; /* clear buffer */
374 ta_set_tr (); /* set tra req */
375 uptr->FNC |= TACS_2ND; /* next state */
376 sim_activate (uptr, ta_ctime); /* sched next char */
377 return SCPE_OK;
378
379 case TACS_WRITE|TACS_2ND: /* write char */
380 if (ta_cs & TACS_ILBS) { /* CRC seq? */
381 uptr->FNC |= TACS_3RD; /* next state */
382 sim_activate (uptr, ta_stime); /* sched wri done */
383 }
384 else {
385 if ((ta_bptr < TA_MAXFR) && /* room in buf? */
386 ((uptr->pos + ta_bptr) < uptr->capac)) /* room on tape? */
387 ta_xb[ta_bptr++] = ta_odb; /* store char */
388 ta_set_tr (); /* set tra req */
389 sim_activate (uptr, ta_ctime); /* sched next char */
390 }
391 return SCPE_OK;
392
393 case TACS_WRITE|TACS_3RD: /* write CRC */
394 if (ta_bptr) { /* anything to write? */
395 if (st = sim_tape_wrrecf (uptr, ta_xb, ta_bptr)) /* write, err? */
396 r = ta_map_err (uptr, st); /* map error */
397 }
398 break; /* op done */
399
400 case TACS_WFG: /* write file gap */
401 if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */
402 r = ta_map_err (uptr, st); /* map error */
403 break;
404
405 case TACS_REW: /* rewind */
406 sim_tape_rewind (uptr);
407 ta_cs |= TACS_BEOT; /* bot, no error */
408 break;
409
410 case TACS_SRB: /* space rev blk */
411 if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */
412 r = ta_map_err (uptr, st); /* map error */
413 break;
414
415 case TACS_SRF: /* space rev file */
416 while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;
417 if (st == MTSE_TMK) /* if tape mark, */
418 ta_cs |= TACS_EOF; /* set EOF, no err */
419 else r = ta_map_err (uptr, st); /* else map error */
420 break;
421
422 case TACS_SFB: /* space fwd blk */
423 if (st = sim_tape_sprecf (uptr, &tbc)) /* space rev, err? */
424 r = ta_map_err (uptr, st); /* map error */
425 ta_cs |= TACS_CRC; /* CRC sets, no err */
426 break;
427
428 case TACS_SFF: /* space fwd file */
429 while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;
430 if (st == MTSE_TMK) /* if tape mark, */
431 ta_cs |= TACS_EOF; /* set EOF, no err */
432 else r = ta_map_err (uptr, st); /* else map error */
433 break;
434
435 default: /* never get here! */
436 return SCPE_IERR;
437 } /* end case */
438
439 ta_cs |= TACS_RDY; /* set ready */
440 ta_updsta (uptr); /* update status */
441 if (DEBUG_PRS (ta_dev)) fprintf (sim_deb,
442 ">>TA done: op=%o, status = %o, pos=%d\n",
443 uptr->FNC, ta_cs, uptr->pos);
444 return r;
445 }
446
447 /* Update controller status */
448
449 uint32 ta_updsta (UNIT *uptr)
450 {
451 if (uptr == NULL) { /* unit specified? */
452 if ((uptr = ta_busy ()) == NULL) /* use busy */
453 uptr = ta_dev.units + GET_UNIT (ta_cs); /* use sel unit */
454 }
455 else if (ta_cs & TACS_EOF) uptr->UST |= UST_GAP; /* save EOF */
456 if (uptr->flags & UNIT_ATT) ta_cs &= ~TACS_EMP; /* attached? */
457 else ta_cs |= TACS_EMP|TACS_RDY; /* no, empty, ready */
458 if ((ta_cs & TACS_IE) && /* int enabled? */
459 (ta_cs & (TACS_TR|TACS_RDY))) /* req or ready? */
460 SET_INT (TA); /* set int req */
461 else CLR_INT (TA); /* no, clr int req */
462 return ta_cs;
463 }
464
465 /* Set transfer request */
466
467 void ta_set_tr (void)
468 {
469 if (ta_cs & TACS_TR) ta_cs |= (TACS_ERR|TACS_TIM); /* flag still set? */
470 else ta_cs |= TACS_TR; /* set xfr req */
471 if (ta_cs & TACS_IE) SET_INT (TA); /* if ie, int req */
472 return;
473 }
474
475 /* Test if controller busy */
476
477 UNIT *ta_busy (void)
478 {
479 uint32 u;
480 UNIT *uptr;
481
482 for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */
483 uptr = ta_dev.units + u;
484 if (sim_is_active (uptr)) return uptr;
485 }
486 return NULL;
487 }
488
489 /* Calculate CRC on buffer */
490
491 uint32 ta_crc (uint8 *buf, uint32 cnt)
492 {
493 uint32 crc, i, j;
494
495 crc = 0;
496 for (i = 0; i < cnt; i++) {
497 crc = crc ^ (((uint32) buf[i]) << 8);
498 for (j = 0; j < 8; j++) {
499 if (crc & 1) crc = (crc >> 1) ^ 0xA001;
500 else crc = crc >> 1;
501 }
502 }
503 return crc;
504 }
505
506 /* Map error status */
507
508 t_stat ta_map_err (UNIT *uptr, t_stat st)
509 {
510 switch (st) {
511
512 case MTSE_FMT: /* illegal fmt */
513 case MTSE_UNATT: /* unattached */
514 ta_cs |= TACS_ERR|TACS_CRC;
515 case MTSE_OK: /* no error */
516 return SCPE_IERR; /* never get here! */
517
518 case MTSE_TMK: /* end of file */
519 ta_cs |= TACS_ERR|TACS_EOF;
520 break;
521
522 case MTSE_IOERR: /* IO error */
523 ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */
524 if (ta_stopioe) return SCPE_IOERR;
525 break;
526
527 case MTSE_INVRL: /* invalid rec lnt */
528 ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */
529 return SCPE_MTRLNT;
530
531 case MTSE_RECE: /* record in error */
532 case MTSE_EOM: /* end of medium */
533 ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */
534 break;
535
536 case MTSE_BOT: /* reverse into BOT */
537 ta_cs |= TACS_ERR|TACS_BEOT; /* set bot */
538 break;
539
540 case MTSE_WRP: /* write protect */
541 ta_cs |= TACS_ERR|TACS_WLK; /* set wlk err */
542 break;
543 }
544
545 return SCPE_OK;
546 }
547
548 /* Reset routine */
549
550 t_stat ta_reset (DEVICE *dptr)
551 {
552 uint32 u;
553 UNIT *uptr;
554
555 ta_cs = 0;
556 ta_idb = 0;
557 ta_odb = 0;
558 ta_write = 0;
559 ta_bptr = 0;
560 ta_blnt = 0;
561 CLR_INT (TA); /* clear interrupt */
562 for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */
563 uptr = ta_dev.units + u;
564 sim_cancel (uptr); /* cancel activity */
565 sim_tape_reset (uptr); /* reset tape */
566 }
567 if (ta_xb == NULL) ta_xb = (uint8 *) calloc (TA_MAXFR + 2, sizeof (uint8));
568 if (ta_xb == NULL) return SCPE_MEM;
569 return SCPE_OK;
570 }
571
572 /* Attach routine */
573
574 t_stat ta_attach (UNIT *uptr, char *cptr)
575 {
576 t_stat r;
577
578 r = sim_tape_attach (uptr, cptr);
579 if (r != SCPE_OK) return r;
580 ta_updsta (NULL);
581 uptr->UST = 0;
582 return r;
583 }
584
585 /* Detach routine */
586
587 t_stat ta_detach (UNIT* uptr)
588 {
589 t_stat r;
590
591 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* check attached */
592 r = sim_tape_detach (uptr);
593 ta_updsta (NULL);
594 uptr->UST = 0;
595 return r;
596 }