1 /* i7094_mt.c: IBM 7094 magnetic tape simulator
2 
3    Copyright (c) 2003-2012, 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    mt           magtape simulator
27 
28    19-Mar-12    RMS     Fixed declaration of sel_name (Mark Pizzolato)
29    16-Jul-10    RMS     Fixed handling of BSR, BSF (Dave Pitts)
30 */
31 
32 #include "i7094_defs.h"
33 #include "sim_tape.h"
34 
35 #define UST             u3                              /* unit state */
36 #define UCH             u4                              /* channel number */
37 #define MTUF_V_LDN      (MTUF_V_UF + 0)
38 #define MTUF_LDN        (1 << MTUF_V_LDN)
39 #define MT_MAXFR        ((1 << 18) + 2)
40 
41 #define QCHRONO(c,u)    ((cpu_model & I_CT) && \
42                          ((c) == CHRONO_CH) && ((u) == CHRONO_UNIT))
43 
44 uint8 *mtxb[NUM_CHAN] = { NULL };                       /* xfer buffer */
45 uint32 mt_unit[NUM_CHAN];                               /* unit */
46 uint32 mt_bptr[NUM_CHAN];
47 uint32 mt_blnt[NUM_CHAN];
48 t_uint64 mt_chob[NUM_CHAN];
49 uint32 mt_chob_v[NUM_CHAN];
50 uint32 mt_tshort = 2;                                   /* "a few microseconds" */
51 uint32 mt_twef = 25000;                                 /* 50 msec */
52 uint32 mt_tstart = 29000;                               /* 58 msec */
53 uint32 mt_tstop = 10000;                                /* 20 msec */
54 uint32 mt_tword = 50;                                   /* 125 usec */
55 
56 static const uint8 odd_par[64] = {
57     1, 0, 0, 1, 0, 1, 1, 0,
58     0, 1, 1, 0, 1, 0, 0, 1,
59     0, 1, 1, 0, 1, 0, 0, 1,
60     1, 0, 0, 1, 0, 1, 1, 0,
61     0, 1, 1, 0, 1, 0, 0, 1,
62     1, 0, 0, 1, 0, 1, 1, 0,
63     1, 0, 0, 1, 0, 1, 1, 0,
64     0, 1, 1, 0, 1, 0, 0, 1
65     };
66 
67 static const char *tape_stat[] = {
68     "OK", "TMK", "UNATT", "IOERR", "INVRECLNT",
69     "FMT", "BOT", "EOM", "RECERR", "WRPROT"
70     };
71 
72 extern uint32 PC;
73 extern uint32 cpu_model;
74 extern uint32 ind_ioc;
75 extern FILE *sim_deb;
76 extern const char *sel_name[];
77 
78 t_stat mt_chsel (uint32 ch, uint32 sel, uint32 unit);
79 t_stat mt_chwr (uint32 ch, t_uint64 val, uint32 flags);
80 t_stat mt_rec_end (UNIT *uptr);
81 t_stat mt_svc (UNIT *uptr);
82 t_stat mt_reset (DEVICE *dptr);
83 t_stat mt_attach (UNIT *uptr, char *cptr);
84 t_stat mt_boot (int32 unitno, DEVICE *dptr);
85 t_stat mt_map_err (UNIT *uptr, t_stat st);
86 
87 extern uint32 chrono_rd (uint8 *buf, uint32 bufsiz);
88 
89 /* MT data structures
90 
91    mt_dev       MT device descriptor
92    mt_unit      MT unit list
93    mt_reg       MT register list
94    mt_mod       MT modifier list
95 */
96 
97 DIB mt_dib = { &mt_chsel, &mt_chwr };
98 
99 MTAB mt_mod[] = {
100     { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },
101     { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },
102     { MTUF_LDN, 0, "high density", "HIGH", NULL },
103     { MTUF_LDN, MTUF_LDN, "low density", "LOW", NULL },
104     { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
105       &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
106     { 0 }
107     };
108 
109 UNIT mta_unit[] = {
110     { UDATA (NULL, UNIT_DIS, 0) },
111     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
112     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
113     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
114     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
115     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
116     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
117     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
118     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
119     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
120     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }
121     };
122 
123 REG mta_reg[] = {
124     { ORDATA (UNIT, mt_unit[0], 5) },
125     { ORDATA (CHOB, mt_chob[0], 36) },
126     { FLDATA (CHOBV, mt_chob_v[0], 0) },
127     { DRDATA (BPTR, mt_bptr[0], 16), PV_LEFT },
128     { DRDATA (BLNT, mt_blnt[0], 16), PV_LEFT },
129     { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) },
130     { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT },
131     { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT },
132     { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT },
133     { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT },
134     { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT },
135     { URDATA (UST, mta_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) },
136     { URDATA (POS, mta_unit[0].pos, 10, T_ADDR_W, 0,
137               MT_NUMDR + 1, PV_LEFT | REG_RO) },
138     { NULL }
139     };
140 
141 UNIT mtb_unit[] = {
142     { UDATA (NULL, UNIT_DIS, 0) },
143     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
144     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
145     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
146     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
147     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
148     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
149     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
150     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
151     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
152     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }
153     };
154 
155 REG mtb_reg[] = {
156     { ORDATA (UNIT, mt_unit[1], 5) },
157     { ORDATA (CHOB, mt_chob[1], 36) },
158     { FLDATA (CHOBV, mt_chob_v[1], 0) },
159     { DRDATA (BPTR, mt_bptr[1], 16), PV_LEFT },
160     { DRDATA (BLNT, mt_blnt[1], 16), PV_LEFT },
161     { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) },
162     { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT },
163     { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT },
164     { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT },
165     { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT },
166     { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT },
167     { URDATA (UST, mtb_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) },
168     { URDATA (POS, mtb_unit[0].pos, 10, T_ADDR_W, 0,
169               MT_NUMDR + 1, PV_LEFT | REG_RO) },
170     { NULL }
171     };
172 
173 UNIT mtc_unit[] = {
174     { UDATA (NULL, UNIT_DIS, 0) },
175     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
176     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
177     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
178     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
179     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
180     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
181     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
182     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
183     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
184     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }
185     };
186 
187 REG mtc_reg[] = {
188     { ORDATA (UNIT, mt_unit[2], 5) },
189     { ORDATA (CHOB, mt_chob[2], 36) },
190     { FLDATA (CHOBV, mt_chob_v[2], 0) },
191     { DRDATA (BPTR, mt_bptr[2], 16), PV_LEFT },
192     { DRDATA (BLNT, mt_blnt[2], 16), PV_LEFT },
193     { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) },
194     { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT },
195     { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT },
196     { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT },
197     { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT },
198     { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT },
199     { URDATA (UST, mtc_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) },
200     { URDATA (POS, mtc_unit[0].pos, 10, T_ADDR_W, 0,
201               MT_NUMDR + 1, PV_LEFT | REG_RO) },
202     { NULL }
203     };
204 
205 UNIT mtd_unit[] = {
206     { UDATA (NULL, UNIT_DIS, 0) },
207     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
208     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
209     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
210     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
211     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
212     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
213     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
214     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
215     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
216     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }
217     };
218 
219 REG mtd_reg[] = {
220     { ORDATA (UNIT, mt_unit[3], 5) },
221     { ORDATA (CHOB, mt_chob[3], 36) },
222     { FLDATA (CHOBV, mt_chob_v[3], 0) },
223     { DRDATA (BPTR, mt_bptr[3], 16), PV_LEFT },
224     { DRDATA (BLNT, mt_blnt[3], 16), PV_LEFT },
225     { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) },
226     { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT },
227     { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT },
228     { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT },
229     { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT },
230     { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT },
231     { URDATA (UST, mtd_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) },
232     { URDATA (POS, mtd_unit[0].pos, 10, T_ADDR_W, 0,
233               MT_NUMDR + 1, PV_LEFT | REG_RO) },
234     { NULL }
235     };
236 
237 UNIT mte_unit[] = {
238     { UDATA (NULL, UNIT_DIS, 0) },
239     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
240     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
241     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
242     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
243     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
244     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
245     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
246     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
247     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
248     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }
249     };
250 
251 REG mte_reg[] = {
252     { ORDATA (UNIT, mt_unit[4], 5) },
253     { ORDATA (CHOB, mt_chob[4], 36) },
254     { FLDATA (CHOBV, mt_chob_v[4], 0) },
255     { DRDATA (BPTR, mt_bptr[4], 16), PV_LEFT },
256     { DRDATA (BLNT, mt_blnt[4], 16), PV_LEFT },
257     { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) },
258     { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT },
259     { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT },
260     { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT },
261     { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT },
262     { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT },
263     { URDATA (UST, mte_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) },
264     { URDATA (POS, mte_unit[0].pos, 10, T_ADDR_W, 0,
265               MT_NUMDR + 1, PV_LEFT | REG_RO) },
266     { NULL }
267     };
268 
269 UNIT mtf_unit[] = {
270     { UDATA (NULL, UNIT_DIS, 0) },
271     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
272     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
273     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
274     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
275     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
276     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
277     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
278     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
279     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
280     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }
281     };
282 
283 REG mtf_reg[] = {
284     { ORDATA (UNIT, mt_unit[5], 5) },
285     { ORDATA (CHOB, mt_chob[5], 36) },
286     { FLDATA (CHOBV, mt_chob_v[5], 0) },
287     { DRDATA (BPTR, mt_bptr[5], 16), PV_LEFT },
288     { DRDATA (BLNT, mt_blnt[5], 16), PV_LEFT },
289     { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) },
290     { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT },
291     { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT },
292     { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT },
293     { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT },
294     { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT },
295     { URDATA (UST, mtf_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) },
296     { URDATA (POS, mtf_unit[0].pos, 10, T_ADDR_W, 0,
297               MT_NUMDR + 1, PV_LEFT | REG_RO) },
298     { NULL }
299     };
300 
301 UNIT mtg_unit[] = {
302     { UDATA (NULL, UNIT_DIS, 0) },
303     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
304     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
305     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
306     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
307     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
308     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
309     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
310     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
311     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
312     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }
313     };
314 
315 REG mtg_reg[] = {
316     { ORDATA (UNIT, mt_unit[6], 5) },
317     { ORDATA (CHOB, mt_chob[6], 36) },
318     { FLDATA (CHOBV, mt_chob_v[6], 0) },
319     { DRDATA (BPTR, mt_bptr[6], 16), PV_LEFT },
320     { DRDATA (BLNT, mt_blnt[6], 16), PV_LEFT },
321     { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) },
322     { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT },
323     { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT },
324     { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT },
325     { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT },
326     { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT },
327     { URDATA (UST, mtg_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) },
328     { URDATA (POS, mtg_unit[0].pos, 10, T_ADDR_W, 0,
329               MT_NUMDR + 1, PV_LEFT | REG_RO) },
330     { NULL }
331     };
332 
333 UNIT mth_unit[] = {
334     { UDATA (NULL, UNIT_DIS, 0) },
335     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
336     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
337     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
338     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
339     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
340     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
341     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
342     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
343     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) },
344     { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }
345     };
346 
347 REG mth_reg[] = {
348     { ORDATA (UNIT, mt_unit[7], 5) },
349     { ORDATA (CHOB, mt_chob[7], 36) },
350     { FLDATA (CHOBV, mt_chob_v[7], 0) },
351     { DRDATA (BPTR, mt_bptr[7], 16), PV_LEFT },
352     { DRDATA (BLNT, mt_blnt[7], 16), PV_LEFT },
353     { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) },
354     { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT },
355     { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT },
356     { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT },
357     { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT },
358     { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT },
359     { URDATA (UST, mth_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) },
360     { URDATA (POS, mth_unit[0].pos, 10, T_ADDR_W, 0,
361               MT_NUMDR + 1, PV_LEFT | REG_RO) },
362     { NULL }
363     };
364 
365 DEVICE mt_dev[NUM_CHAN] = {
366     {
367     "MTA", mta_unit, mta_reg, mt_mod,
368     MT_NUMDR + 1, 10, 31, 1, 8, 8,
369     NULL, NULL, &mt_reset,
370     &mt_boot, &mt_attach, &sim_tape_detach,
371     &mt_dib, DEV_DEBUG
372     },
373     {
374     "MTB", mtb_unit, mtb_reg, mt_mod,
375     MT_NUMDR + 1, 10, 31, 1, 8, 8,
376     NULL, NULL, &mt_reset,
377     NULL, &mt_attach, &sim_tape_detach,
378     &mt_dib, DEV_DIS|DEV_DEBUG
379     },
380     {
381     "MTC", mtc_unit, mtc_reg, mt_mod,
382     MT_NUMDR + 1, 10, 31, 1, 8, 8,
383     NULL, NULL, &mt_reset,
384     NULL, &mt_attach, &sim_tape_detach,
385     &mt_dib, DEV_DIS|DEV_DEBUG
386     },
387     {
388     "MTD", mtd_unit, mtd_reg, mt_mod,
389     MT_NUMDR + 1, 10, 31, 1, 8, 8,
390     NULL, NULL, &mt_reset,
391     NULL, &mt_attach, &sim_tape_detach,
392     &mt_dib, DEV_DIS|DEV_DEBUG
393     },
394     {
395     "MTE", mte_unit, mte_reg, mt_mod,
396     MT_NUMDR + 1, 10, 31, 1, 8, 8,
397     NULL, NULL, &mt_reset,
398     NULL, &mt_attach, &sim_tape_detach,
399     &mt_dib, DEV_DIS|DEV_DEBUG
400     },
401     {
402     "MTF", mtf_unit, mtf_reg, mt_mod,
403     MT_NUMDR + 1, 10, 31, 1, 8, 8,
404     NULL, NULL, &mt_reset,
405     NULL, &mt_attach, &sim_tape_detach,
406     &mt_dib, DEV_DIS|DEV_DEBUG
407     },
408     {
409     "MTG", mtg_unit, mtg_reg, mt_mod,
410     MT_NUMDR + 1, 10, 31, 1, 8, 8,
411     NULL, NULL, &mt_reset,
412     NULL, &mt_attach, &sim_tape_detach,
413     &mt_dib, DEV_DIS|DEV_DEBUG
414     },
415     {
416     "MTH", mth_unit, mth_reg, mt_mod,
417     MT_NUMDR + 1, 10, 31, 1, 8, 8,
418     NULL, NULL, &mt_reset,
419     NULL, &mt_attach, &sim_tape_detach,
420     &mt_dib, DEV_DIS|DEV_DEBUG
421     }
422     };
423 
424 /* Select controller
425 
426    Inputs:
427         ch      =       channel
428         cmd     =       select command
429         unit    =       unit
430    Outputs:
431         status  =       SCPE_OK if ok
432                         STOP_STALL if busy
433                         error code if error
434 */
435 
436 static const int mt_must_att[CHSL_NUM] = {
437     0, 1, 1, 0, 1, 1, 0, 0,
438     1, 1, 1, 1, 1, 1, 0, 0
439     };
440 
441 static const int mt_will_wrt[CHSL_NUM] = {
442     0, 0, 1, 0, 0, 1, 0, 0,
443     1, 1, 0, 0, 0, 0, 0, 0
444     };
445 
mt_chsel(uint32 ch,uint32 cmd,uint32 unit)446 t_stat mt_chsel (uint32 ch, uint32 cmd, uint32 unit)
447 {
448 UNIT *uptr;
449 uint32 u = unit & 017;
450 
451 if ((ch >= NUM_CHAN) || (cmd == 0) || (cmd >= CHSL_NUM))
452     return SCPE_IERR;                                   /* invalid arg? */
453 if (mt_dev[ch].flags & DEV_DIS)                         /* disabled? */
454     return STOP_NXDEV;
455 if ((u == 0) || (u > MT_NUMDR))                         /* valid unit? */
456     return STOP_NXDEV;
457 uptr = mt_dev[ch].units + u;                            /* get unit ptr */
458 if (uptr->flags & UNIT_DIS)                             /* disabled? */
459     return STOP_NXDEV;
460 if (mt_unit[ch] || sim_is_active (uptr))                /* ctrl or unit busy? */
461     return ERR_STALL;                                   /* stall */
462 if (QCHRONO (ch, u)) {                                  /* Chronolog clock? */
463     if (cmd != CHSL_RDS)                                /* only reads */
464         return STOP_ILLIOP;
465     sim_activate (uptr, mt_tword);                      /* responds quickly */
466     }
467 else {                                                  /* real tape */
468     if (!(uptr->flags & UNIT_ATT) && mt_must_att[cmd])  /* unit unatt? */
469         return SCPE_UNATT;
470     if (sim_tape_wrp (uptr) && mt_will_wrt[cmd])        /* unit wrp && write? */
471         return STOP_WRP;
472     if (DEBUG_PRS (mt_dev[ch]))
473         fprintf (sim_deb, ">>%s%d %s, pos = %d\n",
474                  mt_dev[ch].name, u, sel_name[cmd], uptr->pos);
475 
476     switch (cmd) {                                      /* case on cmd */
477 
478     case CHSL_RDS:
479     case CHSL_WRS:
480         sim_activate (uptr, mt_tstart);                 /* schedule op */
481         break;
482 
483     case CHSL_WEF:                                      /* write eof? */
484         sim_activate (uptr, mt_twef);                   /* schedule op */
485         break;
486 
487     case CHSL_BSR:
488     case CHSL_BSF:                                      /* backspace */
489     case CHSL_REW:
490     case CHSL_RUN:
491     case CHSL_SDN:                                      /* rew, rew/unl, set det */
492         sim_activate (uptr, mt_tshort);                 /* schedule quick event */
493         break;
494 
495     default:
496         return SCPE_IERR;
497         }                                               /* end switch */
498     }                                                   /* end else */
499 
500 uptr->UST = cmd;                                        /* set cmd */
501 mt_unit[ch] = unit & 0777;                              /* save unit */
502 return SCPE_OK;
503 }
504 
505 /* Channel write routine */
506 
mt_chwr(uint32 ch,t_uint64 val,uint32 eorfl)507 t_stat mt_chwr (uint32 ch, t_uint64 val, uint32 eorfl)
508 {
509 int32 k, u;
510 uint8 by, *xb;
511 UNIT *uptr;
512 
513 if (ch >= NUM_CHAN)                                     /* invalid chan? */
514     return SCPE_IERR;
515 xb = mtxb[ch];                                          /* get xfer buf */
516 u = mt_unit[ch] & 017;
517 if ((xb == NULL) || (u > MT_NUMDR))                     /* invalid args? */
518     return SCPE_IERR;
519 uptr = mt_dev[ch].units + u;                            /* get unit */
520 mt_chob[ch] = val & DMASK;                              /* save word from chan */
521 mt_chob_v[ch] = 1;                                      /* set valid */
522 
523 if (uptr->UST == (CHSL_WRS|CHSL_2ND)) {                 /* data write? */
524     for (k = 30;                                        /* proc 6 bytes */
525         (k >= 0) && (mt_bptr[ch] < MT_MAXFR);
526          k = k - 6) {
527         by = (uint8) ((val >> k) & 077);                /* get byte */
528         if ((mt_unit[ch] & 020) == 0) {                 /* BCD? */
529             if (by == 0)                                /* cvt bin 0 */
530                 by = BCD_ZERO;
531             else if (by & 020)                          /* invert zones */
532                 by = by ^ 040;
533             if (!odd_par[by])                           /* even parity */
534                 by = by | 0100;
535             }
536         else if (odd_par[by])                           /* bin, odd par */
537             by = by | 0100;
538         xb[mt_bptr[ch]++] = by;                         /* put in buffer */
539         }
540     if (eorfl)
541         return mt_rec_end (uptr);                       /* EOR? write rec */
542     return SCPE_OK;
543     }
544 return SCPE_IERR;
545 }
546 
547 /* Unit timeout */
548 
mt_svc(UNIT * uptr)549 t_stat mt_svc (UNIT *uptr)
550 {
551 uint32 i, u, ch = uptr->UCH;                            /* get channel number */
552 uint8 by, *xb = mtxb[ch];                               /* get xfer buffer */
553 t_uint64 dat;
554 t_mtrlnt bc;
555 t_stat r;
556 
557 if (xb == NULL)                                         /* valid buffer? */
558     return SCPE_IERR;
559 u = uptr - mt_dev[ch].units;
560 switch (uptr->UST) {                                    /* case on state */
561 
562     case CHSL_RDS:                                      /* read start */
563         if (QCHRONO (ch, mt_unit[ch] & 017))            /* Chronolog clock? */
564             bc = chrono_rd (xb, MT_MAXFR);              /* read clock */
565         else {                                          /* real tape */
566             r = sim_tape_rdrecf (uptr, xb, &bc, MT_MAXFR); /* read record */
567             if ((r = mt_map_err (uptr, r)))             /* map status */
568                 return r;
569             if (mt_unit[ch] == 0)                       /* disconnected? */
570                 return SCPE_OK;
571             }                                           /* end else Chrono */
572         if (!ch6_qconn (ch, mt_unit[ch])) {             /* chan disconnected? */
573             mt_unit[ch] = 0;                            /* clr ctrl busy */
574             return SCPE_OK;
575             }
576         for (i = bc; i < (bc + 6); i++)                 /* extra 0's */
577             xb[i] = 0;
578         mt_bptr[ch] = 0;                                /* set ptr, lnt */
579         mt_blnt[ch] = bc;
580         uptr->UST = CHSL_RDS|CHSL_2ND;                  /* next state */
581         sim_activate (uptr, mt_tword);
582         break;
583 
584     case CHSL_RDS|CHSL_2ND:                             /* read word */
585         for (i = 0, dat = 0; i < 6; i++) {              /* proc 6 bytes */
586             by = xb[mt_bptr[ch]++] & 077;               /* get next byte */
587             if ((mt_unit[ch] & 020) == 0) {             /* BCD? */
588                 if (by == BCD_ZERO)                     /* cvt BCD 0 */
589                     by = 0;
590                 else if (by & 020)                      /* invert zones */
591                     by = by ^ 040;
592                 }
593             dat = (dat << 6) | ((t_uint64) by);
594             }
595         if (mt_bptr[ch] >= mt_blnt[ch]) {               /* end of record? */
596             ch6_req_rd (ch, mt_unit[ch], dat, CH6DF_EOR);
597             uptr->UST = CHSL_RDS|CHSL_3RD;              /* next state */
598             sim_activate (uptr, mt_tstop);              /* long timing */
599             }
600         else {
601             ch6_req_rd (ch, mt_unit[ch], dat, 0);       /* send to channel */
602             sim_activate (uptr, mt_tword);              /* next word */
603             }
604         break;
605 
606     case CHSL_RDS|CHSL_3RD:                             /* end record */
607         if (ch6_qconn (ch, mt_unit[ch])) {              /* ch still conn? */
608             uptr->UST = CHSL_RDS;                       /* initial state */
609             sim_activate (uptr, mt_tshort);             /* sched next record */
610             }
611         else mt_unit[ch] = 0;                           /* clr ctrl busy */
612         if (DEBUG_PRS (mt_dev[ch]))
613             fprintf (sim_deb, ">>%s%d RDS complete, pos = %d, %s\n",
614                      mt_dev[ch].name, u, uptr->pos,
615                      mt_unit[ch]? "continuing": "disconnecting");
616         return SCPE_OK;
617 
618     case CHSL_WRS:                                      /* write start */
619         if (!ch6_qconn (ch, mt_unit[ch])) {             /* chan disconnected? */
620             mt_unit[ch] = 0;                            /* clr ctrl busy */
621             return SCPE_OK;                             /* (writes blank tape) */
622             }
623         mt_bptr[ch] = 0;                                /* init buffer */
624         uptr->UST = CHSL_WRS|CHSL_2ND;                  /* next state */
625         ch6_req_wr (ch, mt_unit[ch]);                   /* request channel */
626         mt_chob[ch] = 0;                                /* clr, inval buffer */
627         mt_chob_v[ch] = 0;
628         sim_activate (uptr, mt_tword);                  /* wait for word */
629         break;
630 
631     case CHSL_WRS|CHSL_2ND:                             /* write word */
632         if (!ch6_qconn (ch, mt_unit[ch]))               /* disconnected? */
633             return mt_rec_end (uptr);                   /* write record */
634         if (mt_chob_v[ch])                              /* valid? clear */
635             mt_chob_v[ch] = 0;
636         else ind_ioc = 1;                               /* no, io check */
637         ch6_req_wr (ch, mt_unit[ch]);                   /* request channel */
638         sim_activate (uptr, mt_tword);                  /* next word */
639         break;
640 
641     case CHSL_WRS|CHSL_3RD:                             /* write stop */
642         if (ch6_qconn (ch, mt_unit[ch])) {              /* chan active? */
643             uptr->UST = CHSL_WRS;                       /* initial state */
644             sim_activate (uptr, mt_tshort);             /* sched next record */
645             }
646         else mt_unit[ch] = 0;                           /* clr ctrl busy */
647         if (DEBUG_PRS (mt_dev[ch]))
648             fprintf (sim_deb, ">>%s%d WRS complete, pos = %d, %s\n",
649                      mt_dev[ch].name, u, uptr->pos,
650                      mt_unit[ch]? "continuing": "disconnecting");
651         return SCPE_OK;
652 
653     case CHSL_BSR: case CHSL_BSF:                       /* backspace */
654         uptr->UST = uptr->UST | CHSL_2ND;               /* set 2nd state */
655         sim_activate (uptr, mt_tstart);                 /* reactivate */
656         ch6_end_nds (ch);                               /* disconnect */
657         return SCPE_OK;
658 
659     case CHSL_BSR|CHSL_2ND:                             /* backspace rec */
660         r = sim_tape_sprecr (uptr, &bc);                /* space backwards */
661         mt_unit[ch] = 0;                                /* clr ctrl busy */
662         if (DEBUG_PRS (mt_dev[ch]))
663             fprintf (sim_deb, ">>%s%d BSR complete, pos = %d\n",
664                      mt_dev[ch].name, u, uptr->pos);
665         if (r == MTSE_TMK)                              /* allow tape mark */
666             return SCPE_OK;
667         return mt_map_err (uptr, r);
668 
669     case CHSL_BSF|CHSL_2ND:                             /* backspace file */
670         while ((r = sim_tape_sprecr (uptr, &bc)) == MTSE_OK) ;
671         mt_unit[ch] = 0;                                /* clr ctrl busy */
672         if (DEBUG_PRS (mt_dev[ch]))
673             fprintf (sim_deb, ">>%s%d BSF complete, pos = %d\n",
674                      mt_dev[ch].name, u, uptr->pos);
675         if (r == MTSE_TMK)                              /* allow tape mark */
676             return SCPE_OK;
677         return mt_map_err (uptr, r);                    /* map others */
678 
679     case CHSL_WEF:                                      /* write eof */
680         r = sim_tape_wrtmk (uptr);                      /* write tape mark */
681         mt_unit[ch] = 0;                                /* clr ctrl busy */
682         ch6_end_nds (ch);                               /* disconnect */
683         if (DEBUG_PRS (mt_dev[ch]))
684             fprintf (sim_deb, ">>%s%d WEF complete, pos = %d\n",
685                      mt_dev[ch].name, u, uptr->pos);
686         return mt_map_err (uptr, r);
687 
688     case CHSL_REW: case CHSL_RUN:                       /* rewind, unload */
689         uptr->UST = uptr->UST | CHSL_2ND;               /* set 2nd state */
690         sim_activate (uptr, mt_tstart);                 /* reactivate */
691         mt_unit[ch] = 0;                                /* clr ctrl busy */
692         ch6_end_nds (ch);                               /* disconnect */
693         return SCPE_OK;
694 
695     case CHSL_REW | CHSL_2ND:
696         sim_tape_rewind (uptr);
697         if (DEBUG_PRS (mt_dev[ch]))
698             fprintf (sim_deb, ">>%s%d REW complete, pos = %d\n",
699                      mt_dev[ch].name, u, uptr->pos);
700         return SCPE_OK;
701 
702     case CHSL_RUN | CHSL_2ND:
703         sim_tape_detach (uptr);
704         if (DEBUG_PRS (mt_dev[ch]))
705             fprintf (sim_deb, ">>%s%d RUN complete, pos = %d\n",
706                      mt_dev[ch].name, u, uptr->pos);
707         return SCPE_OK;
708 
709     case CHSL_SDN:
710         if (mt_unit[ch] & 020)                          /* set density flag */
711             uptr->flags = uptr-> flags & ~MTUF_LDN;
712         else uptr->flags = uptr->flags | MTUF_LDN;
713         mt_unit[ch] = 0;                                /* clr ctrl busy */
714         ch6_end_nds (ch);                               /* disconnect */
715         if (DEBUG_PRS (mt_dev[ch]))
716             fprintf (sim_deb, ">>%s%d SDN complete, pos = %d\n",
717                      mt_dev[ch].name, u, uptr->pos);
718         return SCPE_OK;
719 
720     default:
721         return SCPE_IERR;
722         }
723 
724 return SCPE_OK;
725 }
726 
727 /* End record routine */
728 
mt_rec_end(UNIT * uptr)729 t_stat mt_rec_end (UNIT *uptr)
730 {
731 uint32 ch = uptr->UCH;
732 uint8 *xb = mtxb[ch];
733 t_stat r;
734 
735 if (mt_bptr[ch]) {                                      /* any data? */
736     if (xb == NULL)
737         return SCPE_IERR;
738     r = sim_tape_wrrecf (uptr, xb, mt_bptr[ch]);        /* write record */
739     if ((r = mt_map_err (uptr, r)))                     /* map error */
740         return r;
741     }
742 uptr->UST = CHSL_WRS|CHSL_3RD;                          /* next state */
743 sim_cancel (uptr);                                      /* cancel current */
744 sim_activate (uptr, mt_tstop);                          /* long timing */
745 return SCPE_OK;
746 }
747 
748 /* Map tape error status */
749 
mt_map_err(UNIT * uptr,t_stat st)750 t_stat mt_map_err (UNIT *uptr, t_stat st)
751 {
752 uint32 ch = uptr->UCH;
753 uint32 u = mt_unit[ch];
754 uint32 up = uptr - mt_dev[ch].units;
755 
756 if ((st != MTSE_OK) && DEBUG_PRS (mt_dev[ch]))
757     fprintf (sim_deb, ">>%s%d status = %s, pos = %d\n",
758              mt_dev[ch].name, up, tape_stat[st], uptr->pos);
759 
760 switch (st) {
761 
762     case MTSE_FMT:                                      /* illegal fmt */
763     case MTSE_UNATT:                                    /* not attached */
764         ch6_err_disc (ch, u, CHF_TRC);
765         mt_unit[ch] = 0;                                /* disconnect */
766         return SCPE_IERR;
767 
768     case MTSE_IOERR:                                    /* IO error */
769         ch6_err_disc (ch, u, CHF_TRC);
770         mt_unit[ch] = 0;                                /* disconnect */
771         return SCPE_IOERR;
772 
773     case MTSE_INVRL:                                    /* invalid rec lnt */
774         ch6_err_disc (ch, u, CHF_TRC);
775         mt_unit[ch] = 0;                                /* disconnect */
776         return SCPE_MTRLNT;
777 
778     case MTSE_WRP:                                      /* write protect */
779         ch6_err_disc (ch, u, 0);
780         mt_unit[ch] = 0;                                /* disconnect */
781         return STOP_WRP;
782 
783     case MTSE_EOM:                                      /* end of medium */
784     case MTSE_TMK:                                      /* tape mark */
785         ch6_err_disc (ch, u, CHF_EOF);
786         mt_unit[ch] = 0;                                /* disconnect */
787         break;
788 
789     case MTSE_RECE:                                     /* record in error */
790         ch6_set_flags (ch, u, CHF_TRC);
791         break;
792 
793     case MTSE_BOT:                                      /* reverse into BOT */
794         ch6_set_flags (ch, u, CHF_BOT);
795         break;
796 
797     case MTSE_OK:                                       /* no error */
798         break;
799         }
800 
801 return SCPE_OK;
802 }
803 
804 /* Magtape reset */
805 
mt_reset(DEVICE * dptr)806 t_stat mt_reset (DEVICE *dptr)
807 {
808 uint32 ch = dptr - &mt_dev[0];
809 uint32 j;
810 REG *rptr;
811 UNIT *uptr;
812 
813 if (mtxb[ch] == NULL)
814     mtxb[ch] = (uint8 *) calloc (MT_MAXFR + 6, sizeof (uint8));
815 if (mtxb[ch] == NULL)                                   /* allocate buffer */
816     return SCPE_MEM;
817 rptr = find_reg ("BUF", NULL, dptr);                    /* update reg ptr */
818 if (rptr == NULL)
819     return SCPE_IERR;
820 rptr->loc = (void *) mtxb[ch];
821 mt_unit[ch] = 0;                                        /* clear busy */
822 mt_bptr[ch] = 0;                                        /* clear buf ptrs */
823 mt_blnt[ch] = 0;
824 mt_chob[ch] = 0;
825 mt_chob_v[ch] = 0;
826 for (j = 1; j <= MT_NUMDR; j++) {                       /* for all units */
827     uptr = dptr->units + j;
828     uptr->UST = 0;                                      /* clear state */
829     uptr->UCH = ch;
830     sim_cancel (uptr);                                  /* stop activity */
831     }                                                   /* end for */
832 return SCPE_OK;                                         /* done */
833 }
834 
835 /* Magtape attach */
836 
mt_attach(UNIT * uptr,char * cptr)837 t_stat mt_attach (UNIT *uptr, char *cptr)
838 {
839 uptr->flags = uptr->flags & ~MTUF_LDN;                  /* start as hi den */
840 return sim_tape_attach (uptr, cptr);
841 }
842 
843 /* Magtape boot */
844 
845 #define BOOT_START      01000
846 
847 static const t_uint64 boot_rom[5] = {
848     0076200000000 + U_MTBIN - 1,                        /* RDS MT_binary */
849     0054000000000 + BOOT_START + 4,                     /* RCHA *+3 */
850     0054400000000,                                      /* LCHA 0 */
851     0002100000001,                                      /* TTR 1 */
852     0500003000000,                                      /* IOCT 0,,3 */
853     };
854 
mt_boot(int32 unitno,DEVICE * dptr)855 t_stat mt_boot (int32 unitno, DEVICE *dptr)
856 {
857 uint32 i, chan;
858 extern t_uint64 *M;
859 
860 chan = dptr - &mt_dev[0] + 1;
861 WriteP (BOOT_START, boot_rom[0] + unitno + (chan << 9));
862 for (i = 1; i < 5; i++)
863     WriteP (BOOT_START + i, boot_rom[i]);
864 PC = BOOT_START;
865 return SCPE_OK;
866 }
867