41dc2d373505eeacfea7e9bcace4e69c6e911760
1 /* pdp8_mt.c: PDP-8 magnetic tape simulator
3 Copyright (c) 1993-2006, Robert M Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
28 16-Feb-06 RMS Added tape capacity checking
29 16-Aug-05 RMS Fixed C++ declaration and cast problems
30 18-Mar-05 RMS Added attached test to detach routine
31 25-Apr-03 RMS Revised for extended file support
32 29-Mar-03 RMS Added multiformat support
33 04-Mar-03 RMS Fixed bug in SKTR
34 01-Mar-03 RMS Fixed interrupt handling
35 Revised for magtape library
36 30-Oct-02 RMS Revised BOT handling, added error record handling
37 04-Oct-02 RMS Added DIBs, device number support
38 30-Aug-02 RMS Revamped error handling
39 28-Aug-02 RMS Added end of medium support
40 30-May-02 RMS Widened POS to 32b
41 22-Apr-02 RMS Added maximum record length test
42 06-Jan-02 RMS Changed enable/disable support
43 30-Nov-01 RMS Added read only unit, extended SET/SHOW support
44 24-Nov-01 RMS Changed UST, POS, FLG to arrays
45 25-Apr-01 RMS Added device enable/disable support
46 04-Oct-98 RMS V2.4 magtape format
47 22-Jan-97 RMS V2.3 magtape format
48 01-Jan-96 RMS Rewritten from TM8-E Maintenance Manual
50 Magnetic tapes are represented as a series of variable records
61 If the byte count is odd, the record is padded with an extra byte
62 of junk. File marks are represented by a byte count of 0.
65 #include "pdp8_defs.h"
68 #define MT_NUMDR 8 /* #drives */
69 #define USTAT u3 /* unit status */
70 #define MT_MAXFR (1 << 16) /* max record lnt */
71 #define WC_SIZE (1 << 12) /* max word count */
72 #define WC_MASK (WC_SIZE - 1)
74 /* Command/unit - mt_cu */
76 #define CU_V_UNIT 9 /* unit */
78 #define CU_PARITY 00400 /* parity select */
79 #define CU_IEE 00200 /* error int enable */
80 #define CU_IED 00100 /* done int enable */
81 #define CU_V_EMA 3 /* ext mem address */
83 #define CU_EMA (CU_M_EMA << CU_V_EMA)
84 #define CU_DTY 00002 /* drive type */
85 #define CU_UNPAK 00001 /* 6b vs 8b mode */
86 #define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT)
87 #define GET_EMA(x) (((x) & CU_EMA) << (12 - CU_V_EMA))
89 /* Function - mt_fn */
91 #define FN_V_FNC 9 /* function */
101 #define FN_ERASE 00400 /* erase */
102 #define FN_CRC 00200 /* read CRC */
103 #define FN_GO 00100 /* go */
104 #define FN_INC 00040 /* incr mode */
105 #define FN_RMASK 07700 /* readable bits */
106 #define GET_FNC(x) (((x) >> FN_V_FNC) & FN_M_FNC)
108 /* Status - stored in mt_sta or (*) uptr->USTAT */
110 #define STA_ERR (04000 << 12) /* error */
111 #define STA_REW (02000 << 12) /* *rewinding */
112 #define STA_BOT (01000 << 12) /* *start of tape */
113 #define STA_REM (00400 << 12) /* *offline */
114 #define STA_PAR (00200 << 12) /* parity error */
115 #define STA_EOF (00100 << 12) /* *end of file */
116 #define STA_RLE (00040 << 12) /* rec lnt error */
117 #define STA_DLT (00020 << 12) /* data late */
118 #define STA_EOT (00010 << 12) /* *end of tape */
119 #define STA_WLK (00004 << 12) /* *write locked */
120 #define STA_CPE (00002 << 12) /* compare error */
121 #define STA_ILL (00001 << 12) /* illegal */
122 #define STA_9TK 00040 /* 9 track */
123 /* #define STA_BAD 00020 /* bad tape?? */
124 #define STA_INC 00010 /* increment error */
125 #define STA_LAT 00004 /* lateral par error */
126 #define STA_CRC 00002 /* CRC error */
127 #define STA_LON 00001 /* long par error */
129 #define STA_CLR (FN_RMASK | 00020) /* always clear */
130 #define STA_DYN (STA_REW | STA_BOT | STA_REM | STA_EOF | \
131 STA_EOT | STA_WLK) /* kept in USTAT */
134 extern int32 int_req
, stop_inst
;
135 extern UNIT cpu_unit
;
137 int32 mt_cu
= 0; /* command/unit */
138 int32 mt_fn
= 0; /* function */
139 int32 mt_ca
= 0; /* current address */
140 int32 mt_wc
= 0; /* word count */
141 int32 mt_sta
= 0; /* status register */
142 int32 mt_db
= 0; /* data buffer */
143 int32 mt_done
= 0; /* mag tape flag */
144 int32 mt_time
= 10; /* record latency */
145 int32 mt_stopioe
= 1; /* stop on error */
146 uint8
*mtxb
= NULL
; /* transfer buffer */
149 int32
mt70 (int32 IR
, int32 AC
);
150 int32
mt71 (int32 IR
, int32 AC
);
151 int32
mt72 (int32 IR
, int32 AC
);
152 t_stat
mt_svc (UNIT
*uptr
);
153 t_stat
mt_reset (DEVICE
*dptr
);
154 t_stat
mt_attach (UNIT
*uptr
, char *cptr
);
155 t_stat
mt_detach (UNIT
*uptr
);
156 int32
mt_updcsta (UNIT
*uptr
);
157 int32
mt_ixma (int32 xma
);
158 t_stat
mt_map_err (UNIT
*uptr
, t_stat st
);
159 t_stat
mt_vlock (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
160 UNIT
*mt_busy (void);
161 void mt_set_done (void);
163 /* MT data structures
165 mt_dev MT device descriptor
167 mt_reg MT register list
168 mt_mod MT modifier list
171 DIB mt_dib
= { DEV_MT
, 3, { &mt70
, &mt71
, &mt72
} };
174 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
175 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
176 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
177 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
178 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
179 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
180 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
181 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) }
185 { ORDATA (CMD
, mt_cu
, 12) },
186 { ORDATA (FNC
, mt_fn
, 12) },
187 { ORDATA (CA
, mt_ca
, 12) },
188 { ORDATA (WC
, mt_wc
, 12) },
189 { ORDATA (DB
, mt_db
, 12) },
190 { GRDATA (STA
, mt_sta
, 8, 12, 12) },
191 { ORDATA (STA2
, mt_sta
, 6) },
192 { FLDATA (DONE
, mt_done
, 0) },
193 { FLDATA (INT
, int_req
, INT_V_MT
) },
194 { FLDATA (STOP_IOE
, mt_stopioe
, 0) },
195 { DRDATA (TIME
, mt_time
, 24), PV_LEFT
},
196 { URDATA (UST
, mt_unit
[0].USTAT
, 8, 16, 0, MT_NUMDR
, 0) },
197 { URDATA (POS
, mt_unit
[0].pos
, 10, T_ADDR_W
, 0,
198 MT_NUMDR
, PV_LEFT
| REG_RO
) },
199 { FLDATA (DEVNUM
, mt_dib
.dev
, 6), REG_HRO
},
204 { MTUF_WLK
, 0, "write enabled", "WRITEENABLED", &mt_vlock
},
205 { MTUF_WLK
, MTUF_WLK
, "write locked", "LOCKED", &mt_vlock
},
206 { MTAB_XTD
|MTAB_VUN
, 0, "FORMAT", "FORMAT",
207 &sim_tape_set_fmt
, &sim_tape_show_fmt
, NULL
},
208 { MTAB_XTD
|MTAB_VUN
, 0, "CAPACITY", "CAPACITY",
209 &sim_tape_set_capac
, &sim_tape_show_capac
, NULL
},
210 { MTAB_XTD
|MTAB_VDV
, 0, "DEVNO", "DEVNO",
211 &set_dev
, &show_dev
, NULL
},
216 "MT", mt_unit
, mt_reg
, mt_mod
,
217 MT_NUMDR
, 10, 31, 1, 8, 8,
218 NULL
, NULL
, &mt_reset
,
219 NULL
, &mt_attach
, &mt_detach
,
225 int32
mt70 (int32 IR
, int32 AC
)
230 uptr
= mt_dev
.units
+ GET_UNIT (mt_cu
); /* get unit */
231 switch (IR
& 07) { /* decode IR<9:11> */
234 mt_wc
= AC
; /* load word count */
238 mt_wc
= 0; /* clear word count */
242 mt_ca
= AC
; /* load mem address */
246 mt_ca
= 0; /* clear mem address */
250 if (mt_busy ()) mt_sta
= mt_sta
| STA_ILL
| STA_ERR
; /* busy? illegal op */
251 mt_cu
= AC
; /* load command reg */
252 mt_updcsta (mt_dev
.units
+ GET_UNIT (mt_cu
));
256 if (mt_busy ()) mt_sta
= mt_sta
| STA_ILL
| STA_ERR
; /* busy? illegal op */
257 mt_fn
= AC
; /* load function */
258 if ((mt_fn
& FN_GO
) == 0) { /* go set? */
259 mt_updcsta (uptr
); /* update status */
262 f
= GET_FNC (mt_fn
); /* get function */
263 if (((uptr
->flags
& UNIT_ATT
) == 0) ||
264 sim_is_active (uptr
) ||
265 (((f
== FN_WRITE
) || (f
== FN_WREOF
)) && sim_tape_wrp (uptr
))
266 || (((f
== FN_SPACER
) || (f
== FN_REWIND
)) && sim_tape_bot (uptr
))) {
267 mt_sta
= mt_sta
| STA_ILL
| STA_ERR
; /* illegal op error */
268 mt_set_done (); /* set done */
269 mt_updcsta (uptr
); /* update status */
272 uptr
->USTAT
= uptr
->USTAT
& STA_WLK
; /* clear status */
273 if (f
== FN_UNLOAD
) { /* unload? */
274 detach_unit (uptr
); /* set offline */
275 uptr
->USTAT
= STA_REW
| STA_REM
; /* rewinding, off */
276 mt_set_done (); /* set done */
278 else if (f
== FN_REWIND
) { /* rewind */
279 uptr
->USTAT
= uptr
->USTAT
| STA_REW
; /* rewinding */
280 mt_set_done (); /* set done */
282 else mt_done
= 0; /* clear done */
283 mt_updcsta (uptr
); /* update status */
284 sim_activate (uptr
, mt_time
); /* start io */
288 if (mt_busy ()) mt_sta
= mt_sta
| STA_ILL
| STA_ERR
; /* busy? illegal op */
289 mt_db
= AC
; /* load buffer */
290 mt_set_done (); /* set done */
291 mt_updcsta (uptr
); /* update status */
295 return (stop_inst
<< IOT_V_REASON
) + AC
; /* ill inst */
298 int32
mt71 (int32 IR
, int32 AC
)
302 uptr
= mt_dev
.units
+ GET_UNIT (mt_cu
);
303 switch (IR
& 07) { /* decode IR<9:11> */
306 return mt_wc
; /* read word count */
309 mt_reset (&mt_dev
); /* reset everything */
313 return mt_ca
; /* read mem address */
316 return ((mt_updcsta (uptr
) >> 12) & 07777); /* read status */
319 return mt_cu
; /* read command */
322 return (((mt_fn
& FN_RMASK
) | (mt_updcsta (uptr
) & ~FN_RMASK
))
323 & 07777); /* read function */
326 return mt_db
; /* read data buffer */
329 return (stop_inst
<< IOT_V_REASON
) + AC
; /* ill inst */
332 int32
mt72 (int32 IR
, int32 AC
)
336 uptr
= mt_dev
.units
+ GET_UNIT (mt_cu
); /* get unit */
337 switch (IR
& 07) { /* decode IR<9:11> */
340 return (mt_sta
& STA_ERR
)? IOT_SKP
+ AC
: AC
;
343 return (!mt_busy ())? IOT_SKP
+ AC
: AC
;
346 return mt_done
? IOT_SKP
+ AC
: AC
;
349 return (!sim_is_active (uptr
) &&
350 (uptr
->flags
& UNIT_ATT
))? IOT_SKP
+ AC
: AC
;
353 if (!sim_is_active (uptr
)) mt_reset (&mt_dev
); /* if TUR, zap */
354 else { /* just ctrl zap */
355 mt_sta
= 0; /* clear status */
356 mt_done
= 0; /* clear done */
357 mt_updcsta (uptr
); /* update status */
362 return (stop_inst
<< IOT_V_REASON
) + AC
; /* ill inst */
367 If rewind done, reposition to start of tape, set status
368 else, do operation, set done, interrupt
371 t_stat
mt_svc (UNIT
*uptr
)
373 int32 f
, i
, p
, u
, wc
, xma
;
377 t_stat st
, r
= SCPE_OK
;
379 u
= (int32
) (uptr
- mt_dev
.units
); /* get unit number */
380 f
= GET_FNC (mt_fn
); /* get command */
381 xma
= GET_EMA (mt_cu
) + mt_ca
; /* get mem addr */
382 wc
= WC_SIZE
- mt_wc
; /* get wc */
384 if (uptr
->USTAT
& STA_REW
) { /* rewind? */
385 sim_tape_rewind (uptr
); /* update position */
386 if (uptr
->flags
& UNIT_ATT
) /* still on line? */
387 uptr
->USTAT
= (uptr
->USTAT
& STA_WLK
) | STA_BOT
;
388 else uptr
->USTAT
= STA_REM
;
389 if (u
== GET_UNIT (mt_cu
)) { /* selected? */
390 mt_set_done (); /* set done */
391 mt_updcsta (uptr
); /* update status */
396 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* if not attached */
397 uptr
->USTAT
= STA_REM
; /* unit off line */
398 mt_sta
= mt_sta
| STA_ILL
| STA_ERR
; /* illegal operation */
399 mt_set_done (); /* set done */
400 mt_updcsta (uptr
); /* update status */
401 return IORETURN (mt_stopioe
, SCPE_UNATT
);
404 passed_eot
= sim_tape_eot (uptr
); /* passed eot? */
405 switch (f
) { /* case on function */
407 case FN_READ
: /* read */
408 case FN_CMPARE
: /* read/compare */
409 st
= sim_tape_rdrecf (uptr
, mtxb
, &tbc
, MT_MAXFR
); /* read rec */
410 if (st
== MTSE_RECE
) mt_sta
= mt_sta
| STA_PAR
| STA_ERR
; /* rec in err? */
411 else if (st
!= MTSE_OK
) { /* other error? */
412 r
= mt_map_err (uptr
, st
); /* map error */
413 mt_sta
= mt_sta
| STA_RLE
| STA_ERR
; /* err, eof/eom, tmk */
416 cbc
= (mt_cu
& CU_UNPAK
)? wc
: wc
* 2; /* expected bc */
417 if (tbc
!= cbc
) mt_sta
= mt_sta
| STA_RLE
| STA_ERR
; /* wrong size? */
418 if (tbc
< cbc
) { /* record small? */
419 cbc
= tbc
; /* use smaller */
420 wc
= (mt_cu
& CU_UNPAK
)? cbc
: (cbc
+ 1) / 2;
422 for (i
= p
= 0; i
< wc
; i
++) { /* copy buffer */
423 xma
= mt_ixma (xma
); /* increment xma */
424 mt_wc
= (mt_wc
+ 1) & 07777; /* incr word cnt */
425 if (mt_cu
& CU_UNPAK
) c
= mtxb
[p
++];
427 c1
= mtxb
[p
++] & 077;
428 c2
= mtxb
[p
++] & 077;
431 if ((f
== FN_READ
) && MEM_ADDR_OK (xma
)) M
[xma
] = c
;
432 else if ((f
== FN_CMPARE
) && (M
[xma
] != c
)) {
433 mt_sta
= mt_sta
| STA_CPE
| STA_ERR
;
439 case FN_WRITE
: /* write */
440 tbc
= (mt_cu
& CU_UNPAK
)? wc
: wc
* 2;
441 for (i
= p
= 0; i
< wc
; i
++) { /* copy buf to tape */
442 xma
= mt_ixma (xma
); /* incr mem addr */
443 if (mt_cu
& CU_UNPAK
) mtxb
[p
++] = M
[xma
] & 0377;
445 mtxb
[p
++] = (M
[xma
] >> 6) & 077;
446 mtxb
[p
++] = M
[xma
] & 077;
449 if (st
= sim_tape_wrrecf (uptr
, mtxb
, tbc
)) { /* write rec, err? */
450 r
= mt_map_err (uptr
, st
); /* map error */
451 xma
= GET_EMA (mt_cu
) + mt_ca
; /* restore xma */
453 else mt_wc
= 0; /* ok, clear wc */
457 if (st
= sim_tape_wrtmk (uptr
)) /* write tmk, err? */
458 r
= mt_map_err (uptr
, st
); /* map error */
461 case FN_SPACEF
: /* space forward */
463 mt_wc
= (mt_wc
+ 1) & 07777; /* incr wc */
464 if (st
= sim_tape_sprecf (uptr
, &tbc
)) { /* space rec fwd, err? */
465 r
= mt_map_err (uptr
, st
); /* map error */
468 } while ((mt_wc
!= 0) && (passed_eot
|| !sim_tape_eot (uptr
)));
471 case FN_SPACER
: /* space reverse */
473 mt_wc
= (mt_wc
+ 1) & 07777; /* incr wc */
474 if (st
= sim_tape_sprecr (uptr
, &tbc
)) { /* space rec rev, err? */
475 r
= mt_map_err (uptr
, st
); /* map error */
478 } while (mt_wc
!= 0);
482 if (!passed_eot
&& sim_tape_eot (uptr
)) /* just passed EOT? */
483 uptr
->USTAT
= uptr
->USTAT
| STA_EOT
;
484 mt_cu
= (mt_cu
& ~CU_EMA
) | ((xma
>> (12 - CU_V_EMA
)) & CU_EMA
);
485 mt_ca
= xma
& 07777; /* update mem addr */
486 mt_set_done (); /* set done */
487 mt_updcsta (uptr
); /* update status */
491 /* Update controller status */
493 int32
mt_updcsta (UNIT
*uptr
)
495 mt_sta
= (mt_sta
& ~(STA_DYN
| STA_CLR
)) | (uptr
->USTAT
& STA_DYN
);
496 if (((mt_sta
& STA_ERR
) && (mt_cu
& CU_IEE
)) ||
497 (mt_done
&& (mt_cu
& CU_IED
))) int_req
= int_req
| INT_MT
;
498 else int_req
= int_req
& ~INT_MT
;
502 /* Test if controller busy */
509 for (u
= 0; u
< MT_NUMDR
; u
++) { /* loop thru units */
510 uptr
= mt_dev
.units
+ u
;
511 if (sim_is_active (uptr
) && ((uptr
->USTAT
& STA_REW
) == 0))
517 /* Increment extended memory address */
519 int32
mt_ixma (int32 xma
) /* incr extended ma */
523 v
= ((xma
+ 1) & 07777) | (xma
& 070000); /* wrapped incr */
524 if (mt_fn
& FN_INC
) { /* increment mode? */
525 if (xma
== 077777) mt_sta
= mt_sta
| STA_INC
| STA_ERR
; /* at limit? error */
526 else v
= xma
+ 1; /* else 15b incr */
533 void mt_set_done (void)
535 mt_done
= 1; /* set done */
536 mt_fn
= mt_fn
& ~(FN_CRC
| FN_GO
| FN_INC
); /* clear func<4:6> */
540 /* Map tape error status */
542 t_stat
mt_map_err (UNIT
*uptr
, t_stat st
)
546 case MTSE_FMT
: /* illegal fmt */
547 case MTSE_UNATT
: /* unattached */
548 mt_sta
= mt_sta
| STA_ILL
| STA_ERR
;
549 case MTSE_OK
: /* no error */
550 return SCPE_IERR
; /* never get here! */
552 case MTSE_TMK
: /* end of file */
553 uptr
->USTAT
= uptr
->USTAT
| STA_EOF
; /* set EOF */
554 mt_sta
= mt_sta
| STA_ERR
;
557 case MTSE_IOERR
: /* IO error */
558 mt_sta
= mt_sta
| STA_PAR
| STA_ERR
; /* set par err */
559 if (mt_stopioe
) return SCPE_IOERR
;
562 case MTSE_INVRL
: /* invalid rec lnt */
563 mt_sta
= mt_sta
| STA_PAR
| STA_ERR
; /* set par err */
566 case MTSE_RECE
: /* record in error */
567 case MTSE_EOM
: /* end of medium */
568 mt_sta
= mt_sta
| STA_PAR
| STA_ERR
; /* set par err */
571 case MTSE_BOT
: /* reverse into BOT */
572 uptr
->USTAT
= uptr
->USTAT
| STA_BOT
; /* set status */
573 mt_sta
= mt_sta
| STA_ERR
;
576 case MTSE_WRP
: /* write protect */
577 mt_sta
= mt_sta
| STA_ILL
| STA_ERR
; /* illegal operation */
586 t_stat
mt_reset (DEVICE
*dptr
)
591 mt_cu
= mt_fn
= mt_wc
= mt_ca
= mt_db
= mt_sta
= mt_done
= 0;
592 int_req
= int_req
& ~INT_MT
; /* clear interrupt */
593 for (u
= 0; u
< MT_NUMDR
; u
++) { /* loop thru units */
594 uptr
= mt_dev
.units
+ u
;
595 sim_cancel (uptr
); /* cancel activity */
596 sim_tape_reset (uptr
); /* reset tape */
597 if (uptr
->flags
& UNIT_ATT
) uptr
->USTAT
=
598 (sim_tape_bot (uptr
)? STA_BOT
: 0) |
599 (sim_tape_wrp (uptr
)? STA_WLK
: 0);
600 else uptr
->USTAT
= STA_REM
;
602 if (mtxb
== NULL
) mtxb
= (uint8
*) calloc (MT_MAXFR
, sizeof (uint8
));
603 if (mtxb
== NULL
) return SCPE_MEM
;
609 t_stat
mt_attach (UNIT
*uptr
, char *cptr
)
612 int32 u
= uptr
- mt_dev
.units
; /* get unit number */
614 r
= sim_tape_attach (uptr
, cptr
);
615 if (r
!= SCPE_OK
) return r
;
616 uptr
->USTAT
= STA_BOT
| (sim_tape_wrp (uptr
)? STA_WLK
: 0);
617 if (u
== GET_UNIT (mt_cu
)) mt_updcsta (uptr
);
623 t_stat
mt_detach (UNIT
* uptr
)
625 int32 u
= uptr
- mt_dev
.units
; /* get unit number */
627 if (!(uptr
->flags
& UNIT_ATT
)) return SCPE_OK
; /* check for attached */
628 if (!sim_is_active (uptr
)) uptr
->USTAT
= STA_REM
;
629 if (u
== GET_UNIT (mt_cu
)) mt_updcsta (uptr
);
630 return sim_tape_detach (uptr
);
633 /* Write lock/enable routine */
635 t_stat
mt_vlock (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
637 int32 u
= uptr
- mt_dev
.units
; /* get unit number */
639 if ((uptr
->flags
& UNIT_ATT
) && (val
|| sim_tape_wrp (uptr
)))
640 uptr
->USTAT
= uptr
->USTAT
| STA_WLK
;
641 else uptr
->USTAT
= uptr
->USTAT
& ~STA_WLK
;
642 if (u
== GET_UNIT (mt_cu
)) mt_updcsta (uptr
);