First Commit of my working state
[simh.git] / Interdata / id_mt.c
CommitLineData
196ba1fc
PH
1/* id_mt.c: Interdata magnetic tape simulator\r
2\r
3 Copyright (c) 2001-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 M46-494 dual density 9-track magtape controller\r
27\r
28 16-Feb-06 RMS Added tape capacity checking\r
29 18-Mar-05 RMS Added attached test to detach routine\r
30 07-Dec-04 RMS Added read-only file support\r
31 25-Apr-03 RMS Revised for extended file support\r
32 28-Mar-03 RMS Added multiformat support\r
33 28-Feb-03 RMS Revised for magtape library\r
34 20-Feb-03 RMS Fixed read to stop selch on error\r
35\r
36 Magnetic tapes are represented as a series of variable 8b records\r
37 of the form:\r
38\r
39 32b record length in bytes - exact number\r
40 byte 0\r
41 byte 1\r
42 :\r
43 byte n-2\r
44 byte n-1\r
45 32b record length in bytes - exact number\r
46\r
47 If the byte count is odd, the record is padded with an extra byte\r
48 of junk. File marks are represented by a single record length of 0.\r
49 End of tape is two consecutive end of file marks.\r
50*/\r
51\r
52#include "id_defs.h"\r
53#include "sim_tape.h"\r
54\r
55#define UST u3 /* unit status */\r
56#define UCMD u4 /* unit command */\r
57#define MT_MAXFR (1 << 24) /* max transfer */\r
58\r
59/* Command - in UCMD */\r
60\r
61#define MTC_SPCR 0x11 /* backspace */\r
62#define MTC_SKFR 0x13 /* space file rev */\r
63#define MTC_CLR 0x20 /* clear */\r
64#define MTC_RD 0x21 /* read */\r
65#define MTC_WR 0x22 /* write */\r
66#define MTC_SKFF 0x23 /* space file fwd */\r
67#define MTC_WEOF 0x30 /* write eof */\r
68#define MTC_REW 0x38 /* rewind */\r
69#define MTC_MASK 0x3F\r
70#define MTC_STOP1 0x40 /* stop, set EOM */\r
71#define MTC_STOP2 0x80 /* stop, set NMTN */\r
72\r
73/* Status byte, * = in UST */\r
74\r
75#define STA_ERR 0x80 /* error */\r
76#define STA_EOF 0x40 /* end of file */\r
77#define STA_EOT 0x20 /* *end of tape */\r
78#define STA_NMTN 0x10 /* *no motion */\r
79#define STA_UFLGS (STA_EOT|STA_NMTN) /* unit flags */\r
80#define STA_MASK (STA_ERR|STA_EOF|STA_BSY|STA_EOM)\r
81#define SET_EX (STA_ERR|STA_EOF|STA_NMTN)\r
82\r
83extern uint32 int_req[INTSZ], int_enb[INTSZ];\r
84\r
85uint8 mtxb[MT_MAXFR]; /* xfer buffer */\r
86uint32 mt_bptr = 0; /* pointer */\r
87uint32 mt_blnt = 0; /* length */\r
88uint32 mt_sta = 0; /* status byte */\r
89uint32 mt_db = 0; /* data buffer */\r
90uint32 mt_xfr = 0; /* data xfr in prog */\r
91uint32 mt_arm[MT_NUMDR] = { 0 }; /* intr armed */\r
92int32 mt_wtime = 10; /* byte latency */\r
93int32 mt_rtime = 1000; /* record latency */\r
94int32 mt_stopioe = 1; /* stop on error */\r
95uint8 mt_tplte[] = { 0, o_MT0, o_MT0*2, o_MT0*3, TPL_END };\r
96\r
97static const uint8 bad_cmd[64] = {\r
98 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
99 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
100 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
101 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1\r
102 };\r
103\r
104DEVICE mt_dev;\r
105uint32 mt (uint32 dev, uint32 op, uint32 dat);\r
106t_stat mt_svc (UNIT *uptr);\r
107t_stat mt_reset (DEVICE *dptr);\r
108t_stat mt_attach (UNIT *uptr, char *cptr);\r
109t_stat mt_detach (UNIT *uptr);\r
110t_stat mt_boot (int32 unitno, DEVICE *dptr);\r
111t_stat mt_map_err (UNIT *uptr, t_stat st);\r
112\r
113/* MT data structures\r
114\r
115 mt_dev MT device descriptor\r
116 mt_unit MT unit list\r
117 mt_reg MT register list\r
118 mt_mod MT modifier list\r
119*/\r
120\r
121DIB mt_dib = { d_MT, 0, v_MT, mt_tplte, &mt, NULL };\r
122\r
123UNIT mt_unit[] = {\r
124 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r
125 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r
126 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r
127 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }\r
128 };\r
129\r
130REG mt_reg[] = {\r
131 { HRDATA (STA, mt_sta, 8) },\r
132 { HRDATA (BUF, mt_db, 8) },\r
133 { BRDATA (DBUF, mtxb, 16, 8, MT_MAXFR) },\r
134 { HRDATA (DBPTR, mt_bptr, 16) },\r
135 { HRDATA (DBLNT, mt_blnt, 17), REG_RO },\r
136 { FLDATA (XFR, mt_xfr, 0) },\r
137 { GRDATA (IREQ, int_req[l_MT], 16, MT_NUMDR, i_MT) },\r
138 { GRDATA (IENB, int_enb[l_MT], 16, MT_NUMDR, i_MT) },\r
139 { BRDATA (IARM, mt_arm, 16, 1, MT_NUMDR) },\r
140 { FLDATA (STOP_IOE, mt_stopioe, 0) },\r
141 { DRDATA (WTIME, mt_wtime, 24), PV_LEFT + REG_NZ },\r
142 { DRDATA (RTIME, mt_rtime, 24), PV_LEFT + REG_NZ },\r
143 { URDATA (UST, mt_unit[0].UST, 16, 8, 0, MT_NUMDR, 0) },\r
144 { URDATA (CMD, mt_unit[0].UCMD, 16, 8, 0, MT_NUMDR, 0) },\r
145 { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0,\r
146 MT_NUMDR, PV_LEFT | REG_RO) },\r
147 { HRDATA (DEVNO, mt_dib.dno, 8), REG_HRO },\r
148 { HRDATA (SELCH, mt_dib.sch, 1), REG_HRO },\r
149 { NULL }\r
150 };\r
151\r
152MTAB mt_mod[] = {\r
153 { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r
154 { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },\r
155 { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",\r
156 &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },\r
157 { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",\r
158 &sim_tape_set_capac, &sim_tape_show_capac, NULL },\r
159 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",\r
160 &set_dev, &show_dev, NULL },\r
161 { MTAB_XTD|MTAB_VDV, 0, "SELCH", "SELCH",\r
162 &set_sch, &show_sch, NULL },\r
163 { 0 }\r
164 };\r
165\r
166DEVICE mt_dev = {\r
167 "MT", mt_unit, mt_reg, mt_mod,\r
168 MT_NUMDR, 10, 31, 1, 16, 8,\r
169 NULL, NULL, &mt_reset,\r
170 &mt_boot, &mt_attach, &mt_detach,\r
171 &mt_dib, DEV_DISABLE\r
172 };\r
173\r
174/* Magtape: IO routine */\r
175\r
176uint32 mt (uint32 dev, uint32 op, uint32 dat)\r
177{\r
178uint32 i, f, t;\r
179uint32 u = (dev - mt_dib.dno) / o_MT0;\r
180UNIT *uptr = mt_dev.units + u;\r
181\r
182switch (op) { /* case IO op */\r
183\r
184 case IO_ADR: /* select */\r
185 sch_adr (mt_dib.sch, dev); /* inform sel ch */\r
186 return BY; /* byte only */\r
187\r
188 case IO_RD: /* read data */\r
189 if (mt_xfr) mt_sta = mt_sta | STA_BSY; /* xfr? set busy */\r
190 return mt_db; /* return data */\r
191\r
192 case IO_WD: /* write data */\r
193 if (mt_xfr) { /* transfer? */\r
194 mt_sta = mt_sta | STA_BSY; /* set busy */\r
195 if ((uptr->UCMD & (MTC_STOP1 | MTC_STOP2)) &&\r
196 ((uptr->UCMD & MTC_MASK) == MTC_WR)) /* while stopping? */\r
197 mt_sta = mt_sta | STA_ERR; /* write overrun */\r
198 }\r
199 mt_db = dat & DMASK8; /* store data */\r
200 break;\r
201\r
202 case IO_SS: /* status */\r
203 mt_sta = mt_sta & STA_MASK; /* ctrl status */\r
204 if (uptr->flags & UNIT_ATT) /* attached? */\r
205 t = mt_sta | (uptr->UST & STA_UFLGS); /* yes, unit status */\r
206 else t = mt_sta | STA_DU; /* no, dev unavail */\r
207 if (t & SET_EX) t = t | STA_EX; /* test for ex */\r
208 return t;\r
209\r
210 case IO_OC: /* command */\r
211 mt_arm[u] = int_chg (v_MT + u, dat, mt_arm[u]);\r
212 f = dat & MTC_MASK; /* get cmd */\r
213 if (f == MTC_CLR) { /* clear? */\r
214 mt_reset (&mt_dev); /* reset world */\r
215 break;\r
216 }\r
217 if (((uptr->flags & UNIT_ATT) == 0) || /* ignore if unatt */\r
218 bad_cmd[f] || /* or bad cmd */\r
219 (((f == MTC_WR) || (f == MTC_WEOF)) && /* or write */\r
220 sim_tape_wrp (uptr))) break; /* and protected */\r
221 for (i = 0; i < MT_NUMDR; i++) { /* check other drvs */\r
222 if (sim_is_active (&mt_unit[i]) && /* active? */\r
223 (mt_unit[i].UCMD != MTC_REW)) { /* not rewind? */\r
224 sim_cancel (&mt_unit[i]); /* stop */\r
225 mt_unit[i].UCMD = 0;\r
226 }\r
227 }\r
228 if (sim_is_active (uptr) && /* unit active? */\r
229 !(uptr->UCMD & (MTC_STOP1 | MTC_STOP2))) /* not stopping? */\r
230 break; /* ignore */\r
231 if ((f == MTC_WR) || (f == MTC_REW)) mt_sta = 0;/* write, rew: bsy=0 */\r
232 else mt_sta = STA_BSY; /* bsy=1,nmtn,eom,err=0 */\r
233 mt_bptr = mt_blnt = 0; /* not yet started */\r
234 if ((f == MTC_RD) || (f == MTC_WR)) /* data xfr? */\r
235 mt_xfr = 1; /* set xfr flag */\r
236 else mt_xfr = 0;\r
237 uptr->UCMD = f; /* save cmd */\r
238 uptr->UST = 0; /* clr tape stat */\r
239 sim_activate (uptr, mt_rtime); /* start op */\r
240 break;\r
241 }\r
242\r
243return 0;\r
244}\r
245\r
246/* Unit service\r
247\r
248 A given operation can generate up to three interrupts\r
249\r
250 - EOF generates an interrupt when set (read, space, wreof)\r
251 BUSY will still be set, EOM and NMTN will be clear\r
252 - After operation complete + delay, EOM generates an interrupt\r
253 BUSY will be clear, EOM will be set, NMTN will be clear\r
254 - After a further delay, NMTN generates an interrupt\r
255 BUSY will be clear, EOM and NMTN will be set\r
256\r
257 Rewind generates an interrupt when NMTN sets\r
258*/\r
259\r
260t_stat mt_svc (UNIT *uptr)\r
261{\r
262uint32 i;\r
263int32 u = uptr - mt_dev.units;\r
264uint32 dev = mt_dib.dno + (u * o_MT0);\r
265t_mtrlnt tbc;\r
266t_bool passed_eot;\r
267t_stat st, r = SCPE_OK;\r
268\r
269if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */\r
270 uptr->UCMD = 0; /* clr cmd */\r
271 uptr->UST = 0; /* set status */\r
272 mt_xfr = 0; /* clr op flags */\r
273 mt_sta = STA_ERR | STA_EOM; /* set status */\r
274 if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */\r
275 return IORETURN (mt_stopioe, SCPE_UNATT);\r
276 }\r
277\r
278if (uptr->UCMD & MTC_STOP2) { /* stop, gen NMTN? */\r
279 uptr->UCMD = 0; /* clr cmd */\r
280 uptr->UST = uptr->UST | STA_NMTN; /* set nmtn */\r
281 mt_xfr = 0; /* clr xfr */\r
282 if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r
283 return SCPE_OK;\r
284 }\r
285\r
286if (uptr->UCMD & MTC_STOP1) { /* stop, gen EOM? */\r
287 uptr->UCMD = uptr->UCMD | MTC_STOP2; /* clr cmd */\r
288 mt_sta = (mt_sta & ~STA_BSY) | STA_EOM; /* clr busy, set eom */\r
289 if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r
290 sim_activate (uptr, mt_rtime); /* schedule */\r
291 return SCPE_OK;\r
292 }\r
293\r
294passed_eot = sim_tape_eot (uptr); /* passed EOT? */\r
295switch (uptr->UCMD) { /* case on function */\r
296\r
297 case MTC_REW: /* rewind */\r
298 sim_tape_rewind (uptr); /* reposition */\r
299 uptr->UCMD = 0; /* clr cmd */\r
300 uptr->UST = STA_NMTN | STA_EOT; /* update status */\r
301 mt_sta = mt_sta & ~STA_BSY; /* don't set EOM */\r
302 if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */\r
303 return SCPE_OK;\r
304\r
305/* For read, busy = 1 => buffer empty\r
306 For write, busy = 1 => buffer full\r
307 For read, data transfers continue for the full length of the\r
308 record, or the maximum size of the transfer buffer\r
309 For write, data transfers continue until a write is attempted\r
310 and the buffer is empty\r
311*/\r
312\r
313 case MTC_RD: /* read */\r
314 if (mt_blnt == 0) { /* first time? */\r
315 st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */\r
316 if (st == MTSE_RECE) mt_sta = mt_sta | STA_ERR; /* rec in err? */\r
317 else if (st != SCPE_OK) { /* other error? */\r
318 r = mt_map_err (uptr, st); /* map error */\r
319 if (sch_actv (mt_dib.sch, dev)) /* if sch, stop */\r
320 sch_stop (mt_dib.sch);\r
321 break;\r
322 }\r
323 mt_blnt = tbc; /* set buf lnt */\r
324 }\r
325\r
326 if (sch_actv (mt_dib.sch, dev)) { /* sch active? */\r
327 i = sch_wrmem (mt_dib.sch, mtxb, mt_blnt); /* store rec in mem */\r
328 if (sch_actv (mt_dib.sch, dev)) /* sch still active? */\r
329 sch_stop (mt_dib.sch); /* stop chan, long rd */\r
330 else if (i < mt_blnt) /* process entire rec? */\r
331 mt_sta = mt_sta | STA_ERR; /* no, overrun error */\r
332 }\r
333 else if (mt_bptr < mt_blnt) { /* no, if !eor */\r
334 if (!(mt_sta & STA_BSY)) /* busy still clr? */\r
335 mt_sta = mt_sta | STA_ERR; /* read overrun */\r
336 mt_db = mtxb[mt_bptr++]; /* get next byte */\r
337 mt_sta = mt_sta & ~STA_BSY; /* !busy = buf full */\r
338 if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r
339 sim_activate (uptr, mt_wtime); /* reschedule */\r
340 return SCPE_OK;\r
341 }\r
342 break; /* record done */\r
343\r
344 case MTC_WR: /* write */\r
345 if (sch_actv (mt_dib.sch, dev)) { /* sch active? */\r
346 mt_bptr = sch_rdmem (mt_dib.sch, mtxb, MT_MAXFR); /* get rec */\r
347 if (sch_actv (mt_dib.sch, dev)) /* not done? */\r
348 sch_stop (mt_dib.sch); /* stop chan */\r
349 }\r
350 else if (mt_sta & STA_BSY) { /* no, if !eor */\r
351 if (mt_bptr < MT_MAXFR) /* if room */\r
352 mtxb[mt_bptr++] = mt_db; /* store in buf */\r
353 mt_sta = mt_sta & ~STA_BSY; /* !busy = buf emp */\r
354 if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r
355 sim_activate (uptr, mt_wtime); /* reschedule */\r
356 return SCPE_OK;\r
357 }\r
358\r
359 if (mt_bptr) { /* any chars? */\r
360 if (st = sim_tape_wrrecf (uptr, mtxb, mt_bptr)) /* write, err? */\r
361 r = mt_map_err (uptr, st); /* map error */\r
362 }\r
363 break; /* record done */\r
364\r
365 case MTC_WEOF: /* write eof */\r
366 if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */\r
367 r = mt_map_err (uptr, st); /* map error */\r
368 mt_sta = mt_sta | STA_EOF; /* set eof */\r
369 if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */\r
370 break;\r
371\r
372 case MTC_SKFF: /* skip file fwd */\r
373 while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;\r
374 if (st == MTSE_TMK) { /* stopped by tmk? */\r
375 mt_sta = mt_sta | STA_EOF; /* set eof */\r
376 if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r
377 }\r
378 else r = mt_map_err (uptr, st); /* map error */\r
379 break;\r
380\r
381 case MTC_SKFR: /* skip file rev */\r
382 while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;\r
383 if (st == MTSE_TMK) { /* stopped by tmk? */\r
384 mt_sta = mt_sta | STA_EOF; /* set eof */\r
385 if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r
386 }\r
387 else r = mt_map_err (uptr, st); /* map error */\r
388 break;\r
389\r
390 case MTC_SPCR: /* backspace */\r
391 if (st = sim_tape_sprecr (uptr, &tbc)) /* skip rec rev, err? */\r
392 r = mt_map_err (uptr, st); /* map error */\r
393 break;\r
394 } /* end case */\r
395\r
396if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */\r
397 uptr->UST = uptr->UST | STA_EOT;\r
398uptr->UCMD = uptr->UCMD | MTC_STOP1; /* set stop stage 1 */\r
399sim_activate (uptr, mt_rtime); /* schedule */\r
400return r;\r
401}\r
402\r
403/* Map tape error status */\r
404\r
405t_stat mt_map_err (UNIT *uptr, t_stat st)\r
406{\r
407int32 u = uptr - mt_dev.units;\r
408\r
409switch (st) {\r
410\r
411 case MTSE_FMT: /* illegal fmt */\r
412 case MTSE_UNATT: /* not attached */\r
413 mt_sta = mt_sta | STA_ERR;\r
414 case MTSE_OK: /* no error */\r
415 return SCPE_IERR;\r
416\r
417 case MTSE_TMK: /* end of file */\r
418 mt_sta = mt_sta | STA_EOF; /* set eof */\r
419 if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r
420 break;\r
421\r
422 case MTSE_IOERR: /* IO error */\r
423 mt_sta = mt_sta | STA_ERR; /* set err */\r
424 if (mt_stopioe) return SCPE_IOERR;\r
425 break;\r
426\r
427 case MTSE_INVRL: /* invalid rec lnt */\r
428 mt_sta = mt_sta | STA_ERR;\r
429 return SCPE_MTRLNT;\r
430\r
431 case MTSE_WRP: /* write protect */\r
432 case MTSE_RECE: /* record in error */\r
433 case MTSE_EOM: /* end of medium */\r
434 mt_sta = mt_sta | STA_ERR; /* set err */\r
435 break;\r
436\r
437 case MTSE_BOT: /* reverse into BOT */\r
438 uptr->UST = uptr->UST | STA_EOT; /* set err */\r
439 break;\r
440 } /* end switch */\r
441\r
442return SCPE_OK;\r
443}\r
444\r
445/* Reset routine */\r
446\r
447t_stat mt_reset (DEVICE *dptr)\r
448{\r
449uint32 u;\r
450UNIT *uptr;\r
451\r
452mt_bptr = mt_blnt = 0; /* clr buf */\r
453mt_sta = STA_BSY; /* clr flags */\r
454mt_xfr = 0; /* clr controls */\r
455for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */\r
456 CLR_INT (v_MT + u); /* clear int */\r
457 CLR_ENB (v_MT + u); /* disable int */\r
458 mt_arm[u] = 0; /* disarm int */\r
459 uptr = mt_dev.units + u;\r
460 sim_tape_reset (uptr); /* clear pos flag */\r
461 sim_cancel (uptr); /* cancel activity */\r
462 uptr->UST = (uptr->UST & STA_UFLGS) | STA_NMTN; /* init status */\r
463 uptr->UCMD = 0; /* init cmd */\r
464 }\r
465return SCPE_OK;\r
466}\r
467\r
468/* Attach routine */\r
469\r
470t_stat mt_attach (UNIT *uptr, char *cptr)\r
471{\r
472int32 u = uptr - mt_dev.units;\r
473t_stat r;\r
474\r
475r = sim_tape_attach (uptr, cptr);\r
476if (r != SCPE_OK) return r;\r
477uptr->UST = STA_EOT;\r
478if (mt_arm[u]) SET_INT (v_MT + u);\r
479return r;\r
480}\r
481\r
482/* Detach routine */\r
483\r
484t_stat mt_detach (UNIT* uptr)\r
485{\r
486int32 u = uptr - mt_dev.units;\r
487t_stat r;\r
488\r
489if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;\r
490r = sim_tape_detach (uptr);\r
491if (r != SCPE_OK) return r;\r
492if (mt_arm[u]) SET_INT (v_MT + u);\r
493uptr->UST = 0;\r
494return SCPE_OK;\r
495}\r
496\r
497/* Bootstrap routine */\r
498\r
499#define BOOT_START 0x50\r
500#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8))\r
501\r
502static uint8 boot_rom[] = {\r
503 0xD5, 0x00, 0x00, 0xCF, /* ST: AL CF */\r
504 0x43, 0x00, 0x00, 0x80 /* BR 80 */\r
505 };\r
506\r
507t_stat mt_boot (int32 unitno, DEVICE *dptr)\r
508{\r
509extern uint32 PC, dec_flgs;\r
510extern uint16 decrom[];\r
511extern DIB sch_dib;\r
512uint32 sch_dev;\r
513\r
514if (decrom[0xD5] & dec_flgs) return SCPE_NOFNC; /* AL defined? */\r
515sim_tape_rewind (&mt_unit[unitno]); /* rewind */\r
516sch_dev = sch_dib.dno + mt_dib.sch; /* sch dev # */\r
517IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy boot */\r
518IOWriteB (AL_DEV, mt_dib.dno + (unitno * o_MT0)); /* set dev no for unit */\r
519IOWriteB (AL_IOC, 0xA1); /* set dev cmd */\r
520IOWriteB (AL_SCH, sch_dev); /* set dev no for chan */\r
521PC = BOOT_START;\r
522return SCPE_OK;\r
523}\r