First Commit of my working state
[simh.git] / PDP18B / pdp18b_mt.c
CommitLineData
196ba1fc
PH
1/* pdp18b_mt.c: 18b PDP magnetic tape simulator\r
2\r
3 Copyright (c) 1993-2006, Robert M Supnik\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of Robert M Supnik shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from Robert M Supnik.\r
25\r
26 mt (PDP-9) TC59 magtape\r
27 (PDP-15) TC59D magtape\r
28\r
29 16-Feb-06 RMS Added tape capacity checking\r
30 16-Aug-05 RMS Fixed C++ declaration and cast problems\r
31 18-Mar-05 RMS Added attached test to detach routine\r
32 14-Jan-04 RMS Revised IO device call interface\r
33 25-Apr-03 RMS Revised for extended file support\r
34 28-Mar-03 RMS Added multiformat support\r
35 04-Mar-03 RMS Fixed bug in MTTR\r
36 01-Mar-03 RMS Fixed bug in interrupt handling\r
37 Revised for magtape library\r
38 02-Feb-03 RMS Revised IOT decoding\r
39 30-Oct-02 RMS Revised BOT handling, added error record handling\r
40 05-Oct-02 RMS Added DIB, device number support\r
41 Revamped error recovery\r
42 28-Aug-02 RMS Added end of medium support\r
43 30-May-02 RMS Widened POS to 32b\r
44 22-Apr-02 RMS Added maximum record length test\r
45 06-Jan-02 RMS Revised enabled/disable support\r
46 29-Nov-01 RMS Added read only unit support\r
47 25-Nov-01 RMS Revised interrupt structure\r
48 Changed UST, POS, FLG to arrays\r
49 26-Apr-01 RMS Added device enable/disable support\r
50 15-Feb-01 RMS Fixed 3-cycle data break sequence\r
51 04-Oct-98 RMS V2.4 magtape format\r
52 22-Jan-97 RMS V2.3 magtape format\r
53 29-Jun-96 RMS Added unit enable/disable support\r
54\r
55 Magnetic tapes are represented as a series of variable records\r
56 of the form:\r
57\r
58 32b byte count\r
59 byte 0\r
60 byte 1\r
61 :\r
62 byte n-2\r
63 byte n-1\r
64 32 byte count\r
65\r
66 If the byte count is odd, the record is padded with an extra byte\r
67 of junk. File marks are represented by a byte count of 0.\r
68*/\r
69\r
70#include "pdp18b_defs.h"\r
71#include "sim_tape.h"\r
72\r
73#define MT_NUMDR 8 /* #drives */\r
74#define USTAT u3 /* unit status */\r
75#define MT_MAXFR (1 << 16) /* max record length */\r
76#define MT_WC 032 /* word count */\r
77#define MT_CA 033 /* current addr */\r
78#define WC_SIZE (1 << 12) /* max word count */\r
79#define WC_MASK (WC_SIZE - 1)\r
80\r
81/* Command/unit - mt_cu */\r
82\r
83#define CU_V_UNIT 15 /* unit */\r
84#define CU_M_UNIT 07\r
85#define CU_PARITY 0040000 /* parity select */\r
86#define CU_DUMP 0020000 /* dump mode */\r
87#define CU_ERASE 0010000 /* ext rec gap */\r
88#define CU_V_CMD 9 /* command */\r
89#define CU_M_CMD 07\r
90#define FN_NOP 00\r
91#define FN_REWIND 01\r
92#define FN_READ 02\r
93#define FN_CMPARE 03\r
94#define FN_WRITE 04\r
95#define FN_WREOF 05\r
96#define FN_SPACEF 06\r
97#define FN_SPACER 07\r
98#define CU_IE 0000400 /* interrupt enable */\r
99#define CU_V_TYPE 6 /* drive type */\r
100#define CU_M_TYPE 03\r
101#define TY_9TK 3\r
102#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT)\r
103#define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD)\r
104#define GET_TYPE(x) (((x) >> CU_V_TYPE) & CU_M_TYPE)\r
105#define PACKED(x) (((x) & CU_DUMP) || (GET_TYPE (x) != TY_9TK))\r
106\r
107/* Status - stored in mt_sta or (*) uptr->USTAT */\r
108\r
109#define STA_ERR 0400000 /* error */\r
110#define STA_REW 0200000 /* *rewinding */\r
111#define STA_BOT 0100000 /* *start of tape */\r
112#define STA_ILL 0040000 /* illegal cmd */\r
113#define STA_PAR 0020000 /* parity error */\r
114#define STA_EOF 0010000 /* *end of file */\r
115#define STA_EOT 0004000 /* *end of tape */\r
116#define STA_CPE 0002000 /* compare error */\r
117#define STA_RLE 0001000 /* rec lnt error */\r
118#define STA_DLT 0000400 /* data late */\r
119#define STA_BAD 0000200 /* bad tape */\r
120#define STA_DON 0000100 /* done */\r
121\r
122#define STA_CLR 0000077 /* always clear */\r
123#define STA_DYN (STA_REW | STA_BOT | STA_EOF | STA_EOT)\r
124 /* kept in USTAT */\r
125\r
126extern int32 M[];\r
127extern int32 int_hwre[API_HLVL+1];\r
128extern UNIT cpu_unit;\r
129\r
130int32 mt_cu = 0; /* command/unit */\r
131int32 mt_sta = 0; /* status register */\r
132int32 mt_time = 10; /* record latency */\r
133int32 mt_stopioe = 1; /* stop on error */\r
134int32 mt_log = 0;\r
135uint8 *mtxb = NULL; /* transfer buffer */\r
136\r
137DEVICE mt_dev;\r
138int32 mt (int32 dev, int32 pulse, int32 dat);\r
139int32 mt_iors (void);\r
140t_stat mt_svc (UNIT *uptr);\r
141t_stat mt_reset (DEVICE *dptr);\r
142t_stat mt_attach (UNIT *uptr, char *cptr);\r
143t_stat mt_detach (UNIT *uptr);\r
144int32 mt_updcsta (UNIT *uptr, int32 val);\r
145t_stat mt_map_err (UNIT *uptr, t_stat st);\r
146UNIT *mt_busy (void);\r
147\r
148/* MT data structures\r
149\r
150 mt_dev MT device descriptor\r
151 mt_unit MT unit list\r
152 mt_reg MT register list\r
153 mt_mod MT modifier list\r
154*/\r
155\r
156DIB mt_dib = { DEV_MT, 1, &mt_iors, { &mt } };\r
157\r
158UNIT mt_unit[] = {\r
159 { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },\r
160 { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },\r
161 { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },\r
162 { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },\r
163 { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },\r
164 { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },\r
165 { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },\r
166 { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }\r
167 };\r
168\r
169REG mt_reg[] = {\r
170 { ORDATA (STA, mt_sta, 18) },\r
171 { ORDATA (CMD, mt_cu, 18) },\r
172 { ORDATA (WC, M[MT_WC], 18) },\r
173 { ORDATA (CA, M[MT_CA], 18) },\r
174 { FLDATA (INT, int_hwre[API_MTA], INT_V_MTA) },\r
175 { FLDATA (STOP_IOE, mt_stopioe, 0) },\r
176 { DRDATA (TIME, mt_time, 24), PV_LEFT },\r
177 { URDATA (UST, mt_unit[0].USTAT, 8, 16, 0, MT_NUMDR, 0) },\r
178 { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0,\r
179 MT_NUMDR, PV_LEFT | REG_RO) },\r
180 { FLDATA (LOG, mt_log, 0), REG_HIDDEN },\r
181 { ORDATA (DEVNO, mt_dib.dev, 6), REG_HRO },\r
182 { NULL }\r
183 };\r
184\r
185MTAB mt_mod[] = {\r
186 { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r
187 { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, \r
188 { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",\r
189 &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },\r
190 { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",\r
191 &sim_tape_set_capac, &sim_tape_show_capac, NULL },\r
192 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",\r
193 &set_devno, &show_devno, NULL },\r
194 { 0 }\r
195 };\r
196\r
197DEVICE mt_dev = {\r
198 "MT", mt_unit, mt_reg, mt_mod,\r
199 MT_NUMDR, 10, 31, 1, 8, 8,\r
200 NULL, NULL, &mt_reset,\r
201 NULL, &mt_attach, &mt_detach,\r
202 &mt_dib, DEV_DISABLE\r
203 };\r
204\r
205/* IOT routine */\r
206\r
207int32 mt (int32 dev, int32 pulse, int32 dat)\r
208{\r
209int32 f, sb;\r
210UNIT *uptr;\r
211\r
212uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */\r
213mt_updcsta (uptr, 0); /* update status */\r
214sb = pulse & 060; /* subop */\r
215if (pulse & 01) {\r
216 if ((sb == 000) && (uptr->flags & UNIT_ATT) && /* MTTR */\r
217 !sim_is_active (uptr))\r
218 dat = IOT_SKP | dat;\r
219 else if ((sb == 020) && !mt_busy ()) /* MTCR */\r
220 dat = IOT_SKP | dat;\r
221 else if ((sb == 040) && (mt_sta & (STA_ERR | STA_DON))) /* MTSF */\r
222 dat = IOT_SKP | dat;\r
223 }\r
224if ((pulse & 06) && mt_log)\r
225 printf ("[MT%d: IOT=%o, AC=%o, sta=%o]\n",\r
226 GET_UNIT (mt_cu), 0707300 + pulse, dat, mt_sta);\r
227if (pulse & 02) {\r
228 if (sb == 000) dat = dat | (mt_cu & 0777700); /* MTRC */\r
229 else if (sb == 020) { /* MTAF, MTLC */\r
230 if (!mt_busy ()) mt_cu = mt_sta = 0; /* if not busy, clr */\r
231 mt_sta = mt_sta & ~(STA_ERR | STA_DON); /* clear flags */\r
232 }\r
233 else if (sb == 040) dat = dat | mt_sta; /* MTRS */\r
234 }\r
235if (pulse & 04) {\r
236 if (sb == 000) { /* MTGO */\r
237 f = GET_CMD (mt_cu); /* get function */\r
238 if (mt_busy () ||\r
239 sim_is_active (uptr) ||\r
240 (f == FN_NOP) ||\r
241 (((f == FN_SPACER) || (f == FN_REWIND)) && (uptr->USTAT & STA_BOT)) ||\r
242 (((f == FN_WRITE) || (f == FN_WREOF)) && sim_tape_wrp (uptr)) ||\r
243 ((uptr->flags & UNIT_ATT) == 0))\r
244 mt_sta = mt_sta | STA_ILL | STA_ERR; /* set illegal op */\r
245 else {\r
246 if (f == FN_REWIND) uptr->USTAT = STA_REW; /* rewind? */\r
247 else mt_sta = uptr->USTAT = 0; /* no, clear status */\r
248 sim_activate (uptr, mt_time); /* start io */\r
249 }\r
250 }\r
251 if (sb == 020) /* MTCM, MTLC */\r
252 mt_cu = (mt_cu & 0770700) | (dat & 0777700); /* load status */\r
253 }\r
254mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */\r
255return dat;\r
256}\r
257\r
258/* Unit service\r
259\r
260 If rewind done, reposition to start of tape, set status\r
261 else, do operation, set done, interrupt\r
262*/\r
263\r
264t_stat mt_svc (UNIT *uptr)\r
265{\r
266int32 c, c1, c2, c3, f, i, p, u;\r
267int32 wc, xma;\r
268t_mtrlnt tbc, cbc;\r
269t_bool passed_eot;\r
270t_stat st, r = SCPE_OK;\r
271\r
272u = (int32) (uptr - mt_dev.units); /* get unit number */\r
273f = GET_CMD (mt_cu); /* get command */\r
274wc = WC_SIZE - (M[MT_WC] & WC_MASK); /* word count is 12b */\r
275\r
276if (uptr->USTAT & STA_REW) { /* rewind? */\r
277 sim_tape_rewind (uptr); /* rewind tape */\r
278 if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_BOT;\r
279 else uptr->USTAT = 0;\r
280 if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr, STA_DON);\r
281 if (mt_log) printf ("[MT%d: rewind complete, sta=%o]\n", u, mt_sta);\r
282 return SCPE_OK;\r
283 }\r
284\r
285if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */\r
286 mt_updcsta (uptr, STA_ILL); /* illegal operation */\r
287 return IORETURN (mt_stopioe, SCPE_UNATT);\r
288 }\r
289\r
290passed_eot = sim_tape_eot (uptr); /* passed EOT? */\r
291switch (f) { /* case on function */\r
292\r
293 case FN_READ: /* read */\r
294 case FN_CMPARE: /* read/compare */\r
295 st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */\r
296 if (st == MTSE_RECE) mt_sta = mt_sta | STA_PAR | STA_ERR; /* rec in err? */\r
297 else if (st != MTSE_OK) { /* other error? */\r
298 mt_sta = mt_sta | STA_RLE | STA_ERR; /* set RLE flag */\r
299 r = mt_map_err (uptr, st); /* map error */\r
300 break;\r
301 }\r
302 cbc = PACKED (mt_cu)? wc * 3: wc * 2; /* expected bc */\r
303 if (tbc != cbc) mt_sta = mt_sta | STA_RLE | STA_ERR; /* wrong size? */\r
304 if (tbc < cbc) { /* record small? */\r
305 cbc = tbc; /* use smaller */\r
306 wc = PACKED (mt_cu)? ((tbc + 2) / 3): ((tbc + 1) / 2);\r
307 }\r
308 for (i = p = 0; i < wc; i++) { /* copy buffer */\r
309 M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC, CA */\r
310 M[MT_CA] = (M[MT_CA] + 1) & DMASK;\r
311 xma = M[MT_CA] & AMASK;\r
312 if (PACKED (mt_cu)) { /* packed? */\r
313 c1 = mtxb[p++] & 077;\r
314 c2 = mtxb[p++] & 077;\r
315 c3 = mtxb[p++] & 077;\r
316 c = (c1 << 12) | (c2 << 6) | c3;\r
317 }\r
318 else {\r
319 c1 = mtxb[p++];\r
320 c2 = mtxb[p++];\r
321 c = (c1 << 8) | c2;\r
322 }\r
323 if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c;\r
324 else if ((f == FN_CMPARE) && (c != (M[xma] &\r
325 (PACKED (mt_cu)? DMASK: 0177777)))) {\r
326 mt_updcsta (uptr, STA_CPE);\r
327 break;\r
328 }\r
329 } /* end for */\r
330 break;\r
331\r
332 case FN_WRITE: /* write */\r
333 tbc = PACKED (mt_cu)? wc * 3: wc * 2;\r
334 xma = M[MT_CA] & AMASK; /* get mem addr */\r
335 for (i = p = 0; i < wc; i++) { /* copy buf to tape */\r
336 xma = (xma + 1) & AMASK; /* incr mem addr */\r
337 if (PACKED (mt_cu)) { /* packed? */\r
338 mtxb[p++] = (M[xma] >> 12) & 077;\r
339 mtxb[p++] = (M[xma] >> 6) & 077;\r
340 mtxb[p++] = M[xma] & 077;\r
341 }\r
342 else {\r
343 mtxb[p++] = (M[xma] >> 8) & 0377;\r
344 mtxb[p++] = M[xma] & 0377;\r
345 }\r
346 } /* end for */\r
347 if (st = sim_tape_wrrecf (uptr, mtxb, tbc)) /* write rec, err? */\r
348 r = mt_map_err (uptr, st); /* map error */\r
349 else {\r
350 M[MT_CA] = (M[MT_CA] + wc) & DMASK; /* advance mem addr */\r
351 M[MT_WC] = 0; /* clear word cnt */\r
352 }\r
353 mt_cu = mt_cu & ~CU_ERASE; /* clear erase flag */\r
354 break;\r
355\r
356 case FN_WREOF:\r
357 if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */\r
358 r = mt_map_err (uptr, st); /* map error */\r
359 else uptr->USTAT = STA_EOF;\r
360 mt_cu = mt_cu & ~CU_ERASE; /* clear erase flag */\r
361 break;\r
362\r
363 case FN_SPACEF: /* space forward */\r
364 do {\r
365 M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC */\r
366 if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */\r
367 r = mt_map_err (uptr, st); /* map error */\r
368 break;\r
369 }\r
370 } while ((M[MT_WC] != 0) && (passed_eot || !sim_tape_eot (uptr)));\r
371 break;\r
372\r
373 case FN_SPACER: /* space reverse */\r
374 do {\r
375 M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC */\r
376 if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */\r
377 r = mt_map_err (uptr, st); /* map error */\r
378 break;\r
379 }\r
380 } while (M[MT_WC] != 0);\r
381 break;\r
382 } /* end case */\r
383\r
384if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */\r
385 uptr->USTAT = uptr->USTAT | STA_EOT;\r
386mt_updcsta (uptr, STA_DON); /* set done */\r
387if (mt_log) printf ("MT%d: fnc=%d done, ma=%o, wc=%o, sta=%o]\n",\r
388 u, f, M[MT_CA], M[MT_WC], mt_sta);\r
389return r;\r
390}\r
391\r
392/* Update controller status */\r
393\r
394int32 mt_updcsta (UNIT *uptr, int32 news)\r
395{\r
396mt_sta = (mt_sta & ~(STA_DYN | STA_CLR)) |\r
397 (uptr->USTAT & STA_DYN) | news;\r
398if ((mt_sta & (STA_ERR | STA_DON)) && (mt_cu & CU_IE))\r
399 SET_INT (MTA);\r
400else CLR_INT (MTA); /* int request */\r
401return mt_sta;\r
402}\r
403\r
404/* Test if controller busy */\r
405\r
406UNIT *mt_busy (void)\r
407{\r
408int32 u;\r
409UNIT *uptr;\r
410\r
411for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */\r
412 uptr = mt_dev.units + u;\r
413 if (sim_is_active (uptr) && ((uptr->USTAT & STA_REW) == 0))\r
414 return uptr;\r
415 }\r
416return NULL;\r
417}\r
418\r
419/* Map tape error status */\r
420\r
421t_stat mt_map_err (UNIT *uptr, t_stat st)\r
422{\r
423switch (st) {\r
424\r
425 case MTSE_FMT: /* illegal fmt */\r
426 case MTSE_UNATT: /* not attached */\r
427 mt_sta = mt_sta | STA_ILL | STA_ERR;\r
428 case MTSE_OK: /* no error */\r
429 return SCPE_IERR;\r
430\r
431 case MTSE_TMK: /* end of file */\r
432 uptr->USTAT = uptr->USTAT | STA_EOF; /* set EOF */\r
433 mt_sta = mt_sta | STA_ERR;\r
434 break;\r
435\r
436 case MTSE_IOERR: /* IO error */\r
437 mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */\r
438 if (mt_stopioe) return SCPE_IOERR;\r
439 break;\r
440\r
441 case MTSE_INVRL: /* invalid rec lnt */\r
442 mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */\r
443 return SCPE_MTRLNT;\r
444\r
445 case MTSE_RECE: /* record in error */\r
446 mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */\r
447 break;\r
448\r
449 case MTSE_EOM: /* end of medium */\r
450 mt_sta = mt_sta | STA_BAD | STA_ERR; /* set end tape */\r
451 break;\r
452\r
453 case MTSE_BOT: /* reverse into BOT */\r
454 uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */\r
455 mt_sta = mt_sta | STA_ERR;\r
456 break;\r
457\r
458 case MTSE_WRP: /* write protect */\r
459 mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */\r
460 break;\r
461 }\r
462\r
463return SCPE_OK;\r
464}\r
465\r
466/* Reset routine */\r
467\r
468t_stat mt_reset (DEVICE *dptr)\r
469{\r
470int32 u;\r
471UNIT *uptr;\r
472\r
473mt_cu = mt_sta = 0;\r
474for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */\r
475 uptr = mt_dev.units + u;\r
476 sim_tape_reset (uptr); /* reset tape */\r
477 sim_cancel (uptr); /* cancel activity */\r
478 if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_BOT;\r
479 else uptr->USTAT = 0;\r
480 }\r
481mt_updcsta (&mt_unit[0], 0); /* update status */\r
482if (mtxb == NULL) mtxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8));\r
483if (mtxb == NULL) return SCPE_MEM;\r
484return SCPE_OK;\r
485}\r
486\r
487/* IORS routine */\r
488\r
489int32 mt_iors (void)\r
490{\r
491return (mt_sta & (STA_ERR | STA_DON))? IOS_MTA: 0;\r
492}\r
493\r
494/* Attach routine */\r
495\r
496t_stat mt_attach (UNIT *uptr, char *cptr)\r
497{\r
498t_stat r;\r
499\r
500r = sim_tape_attach (uptr, cptr);\r
501if (r != SCPE_OK) return r;\r
502uptr->USTAT = STA_BOT;\r
503mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */\r
504return r;\r
505}\r
506\r
507/* Detach routine */\r
508\r
509t_stat mt_detach (UNIT* uptr)\r
510{\r
511if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */\r
512if (!sim_is_active (uptr)) uptr->USTAT = 0;\r
513mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */\r
514return sim_tape_detach (uptr);\r
515}\r