First Commit of my working state
[simh.git] / SDS / sds_mt.c
CommitLineData
196ba1fc
PH
1/* sds_mt.c: SDS 940 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 7 track magnetic tape\r
27\r
28 16-Feb-06 RMS Added tape capacity checking\r
29 07-Dec-04 RMS Added read-only file support\r
30 25-Apr-03 RMS Revised for extended file support\r
31 28-Mar-03 RMS Added multiformat support\r
32 28-Feb-03 RMS Revised for magtape library\r
33\r
34 Magnetic tapes are represented as a series of variable 8b records\r
35 of the form:\r
36\r
37 32b record length in bytes - exact number\r
38 byte 0\r
39 byte 1\r
40 :\r
41 byte n-2\r
42 byte n-1\r
43 32b record length in bytes - exact number\r
44\r
45 If the byte count is odd, the record is padded with an extra byte\r
46 of junk. File marks are represented by a single record length of 0.\r
47 End of tape is two consecutive end of file marks.\r
48*/\r
49\r
50#include "sds_defs.h"\r
51#include "sim_tape.h"\r
52\r
53#define MT_MAXFR (32768 * 4)\r
54#define MT_NUMDR 8 /* number drives */\r
55#define MT_UNIT 07\r
56#define botf u3 /* bot tape flag */\r
57#define eotf u4 /* eot tape flag */\r
58\r
59extern uint32 xfr_req;\r
60extern int32 stop_invins, stop_invdev, stop_inviop;\r
61int32 mt_inst = 0; /* saved instr */\r
62int32 mt_eof = 0; /* end of file */\r
63int32 mt_gap = 0; /* in gap */\r
64int32 mt_skip = 0; /* skip rec */\r
65int32 mt_bptr = 0; /* buf ptr */\r
66int32 mt_blnt = 0; /* buf length */\r
67int32 mt_ctime = 10; /* char time */\r
68int32 mt_gtime = 1000; /* gap time */\r
69int32 mt_stopioe = 1; /* stop on err */\r
70uint8 mtxb[MT_MAXFR]; /* record buffer */\r
71DSPT mt_tplt[] = { /* template */\r
72 { MT_NUMDR, 0 },\r
73 { MT_NUMDR, DEV_MTS },\r
74 { MT_NUMDR, DEV_OUT },\r
75 { MT_NUMDR, DEV_MTS+DEV_OUT },\r
76 { 0, 0 }\r
77 };\r
78\r
79DEVICE mt_dev;\r
80t_stat mt_svc (UNIT *uptr);\r
81t_stat mt_reset (DEVICE *dptr);\r
82t_stat mt_boot (int32 unitno, DEVICE *dptr);\r
83t_stat mt_attach (UNIT *uptr, char *cptr);\r
84t_stat mt_detach (UNIT *uptr);\r
85t_stat mt_readrec (UNIT *uptr);\r
86t_mtrlnt mt_readbc (UNIT *uptr);\r
87void mt_readend (UNIT *uptr);\r
88t_stat mt_wrend (uint32 dev);\r
89void mt_set_err (UNIT *uptr);\r
90t_stat mt (uint32 fnc, uint32 inst, uint32 *dat);\r
91\r
92static const char sds_to_bcd[64] = {\r
93 012, 001, 002, 003, 004, 005, 006, 007,\r
94 010, 011, 012, 013, 014, 015, 016, 017,\r
95 060, 061, 062, 063, 064, 065, 066, 067,\r
96 070, 071, 072, 073, 074, 075, 076, 077,\r
97 040, 041, 042, 043, 044, 045, 046, 047,\r
98 050, 051, 052, 053, 054, 055, 056, 057,\r
99 020, 021, 022, 023, 024, 025, 026, 027,\r
100 030, 031, 032, 033, 034, 035, 036, 037\r
101 };\r
102\r
103static const char bcd_to_sds[64] = {\r
104 000, 001, 002, 003, 004, 005, 006, 007,\r
105 010, 011, 000, 013, 014, 015, 016, 017,\r
106 060, 061, 062, 063, 064, 065, 066, 067,\r
107 070, 071, 072, 073, 074, 075, 076, 077,\r
108 040, 041, 042, 043, 044, 045, 046, 047,\r
109 050, 051, 052, 053, 054, 055, 056, 057,\r
110 020, 021, 022, 023, 024, 025, 026, 027,\r
111 030, 031, 032, 033, 034, 035, 036, 037\r
112 };\r
113\r
114/* MT data structures\r
115\r
116 mt_dev MT device descriptor\r
117 mt_unit MT unit descriptor\r
118 mt_reg MT register list\r
119*/\r
120\r
121DIB mt_dib = { CHAN_W, DEV_MT, XFR_MT0, mt_tplt, &mt };\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 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r
129 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r
130 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r
131 { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }\r
132 };\r
133\r
134REG mt_reg[] = {\r
135 { BRDATA (BUF, mtxb, 8, 8, MT_MAXFR) },\r
136 { DRDATA (BPTR, mt_bptr, 18), PV_LEFT },\r
137 { DRDATA (BLNT, mt_blnt, 18), PV_LEFT },\r
138 { FLDATA (XFR, xfr_req, XFR_V_MT0) },\r
139 { ORDATA (INST, mt_inst, 24) },\r
140 { FLDATA (EOF, mt_eof, 0) },\r
141 { FLDATA (GAP, mt_gap, 0) },\r
142 { FLDATA (SKIP, mt_skip, 0) },\r
143 { DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT },\r
144 { DRDATA (GTIME, mt_gtime, 24), REG_NZ + PV_LEFT },\r
145 { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0,\r
146 MT_NUMDR, PV_LEFT | REG_RO) },\r
147 { URDATA (BOT, mt_unit[0].botf, 10, 1, 0, MT_NUMDR, REG_RO) },\r
148 { URDATA (EOT, mt_unit[0].eotf, 10, 1, 0, MT_NUMDR, REG_RO) },\r
149 { FLDATA (STOP_IOE, mt_stopioe, 0) },\r
150 { NULL }\r
151 };\r
152\r
153MTAB mt_mod[] = {\r
154 { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r
155 { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, \r
156 { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",\r
157 &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },\r
158 { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",\r
159 &sim_tape_set_capac, &sim_tape_show_capac, NULL },\r
160 { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",\r
161 &set_chan, &show_chan, NULL },\r
162 { 0 }\r
163 };\r
164\r
165DEVICE mt_dev = {\r
166 "MT", mt_unit, mt_reg, mt_mod,\r
167 MT_NUMDR, 10, 31, 1, 8, 8,\r
168 NULL, NULL, &mt_reset,\r
169 &mt_boot, &mt_attach, NULL,\r
170 &mt_dib, DEV_DISABLE\r
171 };\r
172\r
173/* Mag tape routine\r
174\r
175 conn - inst = EOM0, dat = NULL\r
176 eom1 - inst = EOM1, dat = NULL\r
177 sks - inst = SKS, dat = ptr to result\r
178 disc - inst = device number, dat = NULL\r
179 wreor - inst = device number, dat = NULL\r
180 read - inst = device number, dat = ptr to data\r
181 write - inst = device number, dat = ptr to result\r
182*/\r
183\r
184t_stat mt (uint32 fnc, uint32 inst, uint32 *dat)\r
185{\r
186int32 u = inst & MT_UNIT; /* get unit */\r
187UNIT *uptr = mt_dev.units + u; /* get unit ptr */\r
188int32 t, new_ch;\r
189uint8 chr;\r
190t_stat r;\r
191\r
192switch (fnc) { /* case function */\r
193\r
194 case IO_CONN: /* connect */\r
195 new_ch = I_GETEOCH (inst); /* get new chan */\r
196 if (new_ch != mt_dib.chan) return SCPE_IERR; /* wrong chan? */\r
197 if (mt_gap) { /* in gap? */\r
198 mt_gap = 0; /* clr gap flg */\r
199 sim_cancel (uptr); /* cancel timer */\r
200 }\r
201 else if (sim_is_active (uptr)) CRETIOP; /* busy? */\r
202 uptr->eotf = 0; /* clr eot flag */ \r
203 mt_eof = 0; /* clr eof flag */\r
204 mt_skip = 0; /* clr skp flag */\r
205 mt_bptr = mt_blnt = 0; /* init buffer */\r
206 if ((inst & DEV_MTS)? (CHC_GETCPW (inst) < 2): /* scn & cpw<3? */\r
207 (inst & CHC_REV)) return STOP_INVIOP; /* rw & rev? */\r
208 mt_inst = inst; /* save inst */\r
209 if ((inst & DEV_MTS) && !(inst && DEV_OUT)) /* scanning? */\r
210 chan_set_flag (mt_dib.chan, CHF_SCAN); /* set chan flg */\r
211 xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */\r
212 sim_activate (uptr, mt_gtime); /* start timer */\r
213 break;\r
214\r
215 case IO_EOM1: /* EOM mode 1 */\r
216 new_ch = I_GETEOCH (inst); /* get new chan */\r
217 if (new_ch != mt_dib.chan) CRETIOP; /* wrong chan? */\r
218 t = inst & 07670; /* get command */\r
219 if ((t == 04010) && !sim_is_active (uptr)) { /* rewind? */\r
220 sim_tape_rewind (uptr); /* rewind unit */\r
221 uptr->eotf = 0; /* clr eot */\r
222 uptr->botf = 1; /* set bot */\r
223 }\r
224 else if ((t == 03610) && sim_is_active (uptr) &&/* skip rec? */\r
225 ((mt_inst & DEV_OUT) == 0)) mt_skip = 1; /* set flag */\r
226 else CRETINS;\r
227 break;\r
228\r
229 case IO_DISC: /* disconnect */\r
230 sim_cancel (uptr); /* no more xfr's */\r
231 if (inst & DEV_OUT) { /* write? */\r
232 if (r = mt_wrend (inst)) return r; /* end record */\r
233 }\r
234 break;\r
235\r
236 case IO_WREOR: /* write eor */\r
237 chan_set_flag (mt_dib.chan, CHF_EOR); /* set eor flg */\r
238 if (r = mt_wrend (inst)) return r; /* end record */\r
239 mt_gap = 1; /* in gap */\r
240 sim_activate (uptr, mt_gtime); /* start timer */ \r
241 break;\r
242\r
243 case IO_SKS: /* SKS */\r
244 new_ch = I_GETSKCH (inst); /* get chan # */\r
245 if (new_ch != mt_dib.chan) return SCPE_IERR; /* wrong chan? */\r
246 if ((inst & (DEV_OUT | DEV_MTS)) == 0) { /* not sks 1n? */\r
247 t = I_GETSKCND (inst); /* get skip cond */\r
248 switch (t) { /* case sks cond */\r
249 case 001: /* sks 1021n */\r
250 *dat = 1; /* not magpak */\r
251 break;\r
252 case 002: /* sks 1041n */\r
253 if (!(uptr->flags & UNIT_ATT) || /* not ready */\r
254 sim_is_active (uptr)) *dat = 1;\r
255 break;\r
256 case 004: /* sks 1101n */\r
257 if (!uptr->eotf) *dat = 1; /* not EOT */\r
258 break;\r
259 case 010: /* sks 1201n */\r
260 if (!uptr->botf) *dat = 1; /* not BOT */\r
261 break;\r
262 case 013: /* sks 12610 */\r
263 if (!mt_gap) *dat = 1; /* not in gap */\r
264 break;\r
265 case 017: /* sks 13610 */\r
266 if (!mt_eof) *dat = 1; /* not EOF */\r
267 break;\r
268 case 020: /* sks 1401n */\r
269 if (!sim_tape_wrp (uptr)) *dat = 1; /* not wrp */\r
270 break;\r
271 case 031: /* sks 1621n */\r
272 case 033: /* sks 1661n */\r
273 *dat = 1; /* not 556bpi */\r
274 case 035: /* sks 1721n */\r
275 break; /* not 800bpi */\r
276 }\r
277 } /* end if */\r
278 break;\r
279\r
280 case IO_READ: /* read */\r
281 xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */\r
282 if (mt_blnt == 0) { /* first read? */\r
283 r = mt_readrec (uptr); /* get data */\r
284 if ((r != SCPE_OK) || (mt_blnt == 0)) return r; /* err, inv reclnt? */\r
285 }\r
286 uptr->botf = 0; /* off BOT */\r
287 if (mt_inst & CHC_REV) chr = mtxb[--mt_bptr] & 077; /* get next rev */\r
288 else chr = mtxb[mt_bptr++] & 077; /* get next fwd */\r
289 if (!(mt_inst & CHC_BIN)) chr = bcd_to_sds[chr];/* bcd? */\r
290 *dat = chr & 077; /* give to chan */\r
291 if ((mt_inst & CHC_REV)? (mt_bptr <= 0): /* rev or fwd, */\r
292 (mt_bptr >= mt_blnt)) mt_readend (uptr); /* recd done? */\r
293 break;\r
294\r
295 case IO_WRITE: /* write */\r
296 uptr->botf = 0; /* off BOT */\r
297 chr = (*dat) & 077;\r
298 xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */\r
299 if (!(mt_inst & CHC_BIN)) chr = sds_to_bcd[chr];/* bcd? */\r
300 if (mt_bptr < MT_MAXFR) mtxb[mt_bptr++] = chr; /* insert in buf */\r
301 break;\r
302\r
303 default:\r
304 CRETINS;\r
305 }\r
306\r
307return SCPE_OK;\r
308} \r
309\r
310/* Unit service */\r
311\r
312t_stat mt_svc (UNIT *uptr)\r
313{\r
314if (mt_gap) { /* gap timeout */\r
315 mt_gap = 0; /* clr gap flg */\r
316 chan_disc (mt_dib.chan); /* disc chan */\r
317 }\r
318else if (mt_skip) mt_readend (uptr); /* skip record */\r
319else { /* normal xfr */\r
320 xfr_req = xfr_req | XFR_MT0; /* set xfr req */\r
321 sim_activate (uptr, mt_ctime); /* reactivate */\r
322 }\r
323return SCPE_OK;\r
324}\r
325\r
326/* Read start (get new record) */\r
327\r
328t_stat mt_readrec (UNIT *uptr)\r
329{\r
330t_mtrlnt tbc;\r
331t_stat st;\r
332\r
333if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */\r
334 mt_set_err (uptr); /* no, err, disc */\r
335 return SCPE_UNATT;\r
336 }\r
337if (mt_inst & CHC_REV) /* reverse? */\r
338 st = sim_tape_rdrecr (uptr, mtxb, &tbc, MT_MAXFR); /* read rec rev */\r
339else { /* no, fwd */\r
340 t_bool passed_eot = sim_tape_eot (uptr); /* passed EOT? */\r
341 st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR);\r
342 if (!passed_eot && sim_tape_eot (uptr)) /* just passed eot? */\r
343 uptr->eotf = 1;\r
344 }\r
345if (st == MTSE_TMK) { /* tape mark? */\r
346 mt_eof = 1; /* set eof flag */\r
347 mtxb[0] = mtxb[1] = 017; /* EOR char */\r
348 mt_blnt = 2; /* store 2 */\r
349 return SCPE_OK;\r
350 }\r
351if (st != MTSE_OK) { /* other error? */\r
352 mt_set_err (uptr); /* err, disc */\r
353 if (st == MTSE_IOERR) return SCPE_IOERR; /* IO error? */\r
354 if (st == MTSE_INVRL) return SCPE_MTRLNT; /* inv rec lnt? */\r
355 if (st == MTSE_EOM) uptr->eotf = 1; /* eom? set eot */\r
356 return SCPE_OK;\r
357 }\r
358mt_blnt = tbc; /* set buf lnt */\r
359return SCPE_OK;\r
360}\r
361\r
362/* Read done (eof, end of record) */\r
363\r
364void mt_readend (UNIT *uptr)\r
365{\r
366sim_cancel (uptr); /* stop timer */\r
367mt_skip = 0; /* clr skp flg */\r
368chan_set_flag (mt_dib.chan, CHF_EOR); /* end record */\r
369if (mt_eof) chan_disc (mt_dib.chan); /* EOF? */\r
370else {\r
371 mt_gap = 1; /* no, in gap */\r
372 sim_activate (uptr, mt_gtime); /* start timer */\r
373 }\r
374return;\r
375}\r
376\r
377/* Write complete (end of record or disconnect) */\r
378\r
379t_stat mt_wrend (uint32 dev)\r
380{\r
381UNIT *uptr = mt_dev.units + (dev & MT_UNIT);\r
382t_mtrlnt tbc;\r
383t_stat st;\r
384\r
385sim_cancel (uptr); /* no more xfr's */\r
386if (mt_bptr == 0) return SCPE_OK; /* buf empty? */\r
387if (!(uptr->flags & UNIT_ATT)) { /* attached? */\r
388 mt_set_err (uptr); /* no, err, disc */\r
389 return SCPE_UNATT;\r
390 }\r
391if (sim_tape_wrp (uptr)) { /* write lock? */\r
392 mt_set_err (uptr); /* yes, err, disc */\r
393 return SCPE_OK;\r
394 }\r
395if (dev & DEV_MTS) { /* erase? */\r
396 if (mt_inst & CHC_REV) /* reverse? */\r
397 sim_tape_sprecr (uptr, &tbc); /* backspace */\r
398 st = sim_tape_wreom (uptr); /* write eom */\r
399 }\r
400else {\r
401 t_bool passed_eot = sim_tape_eot (uptr); /* passed EOT? */\r
402 if ((mt_bptr == 1) && (mtxb[0] == 017) && /* wr eof? */\r
403 ((mt_inst & 01670) == 00050))\r
404 st = sim_tape_wrtmk (uptr); /* write tape mark */\r
405 else st = sim_tape_wrrecf (uptr, mtxb, mt_bptr); /* write record */\r
406 if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */\r
407 uptr->eotf = 1;\r
408 }\r
409mt_bptr = 0;\r
410if (st != MTSE_OK) mt_set_err (uptr); /* error? */\r
411if (st == MTSE_IOERR) return SCPE_IOERR;\r
412return SCPE_OK;\r
413}\r
414\r
415/* Fatal error */\r
416\r
417void mt_set_err (UNIT *uptr)\r
418{\r
419chan_set_flag (mt_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */\r
420chan_disc (mt_dib.chan); /* disconnect */\r
421xfr_req = xfr_req & ~XFR_MT0; /* clear xfr */\r
422sim_cancel (uptr); /* stop */\r
423mt_bptr = 0; /* buf empty */\r
424return;\r
425}\r
426/* Reset routine */\r
427\r
428t_stat mt_reset (DEVICE *dptr)\r
429{\r
430int32 i;\r
431\r
432chan_disc (mt_dib.chan); /* disconnect */\r
433mt_eof = 0; /* clear state */\r
434mt_gap = 0;\r
435mt_skip = 0;\r
436mt_inst = 0;\r
437mt_bptr = mt_blnt = 0;\r
438xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */\r
439for (i = 0; i < MT_NUMDR; i++) { /* deactivate */\r
440 sim_cancel (&mt_unit[i]);\r
441 sim_tape_reset (&mt_unit[i]);\r
442 mt_unit[i].eotf = 0;\r
443 }\r
444return SCPE_OK;\r
445}\r
446\r
447/* Attach and detach routines */\r
448\r
449t_stat mt_attach (UNIT *uptr, char *cptr)\r
450{\r
451t_stat r;\r
452\r
453r = sim_tape_attach (uptr, cptr);\r
454if (r != SCPE_OK) return r;\r
455uptr->botf = 1;\r
456uptr->eotf = 0;\r
457return SCPE_OK;\r
458}\r
459\r
460t_stat mt_detach (UNIT *uptr)\r
461{\r
462uptr->botf = uptr->eotf = 0;\r
463return sim_tape_detach (uptr);\r
464}\r
465\r
466/* Boot routine - simulate FILL console command */\r
467\r
468t_stat mt_boot (int32 unitno, DEVICE *dptr)\r
469{\r
470extern uint32 P, M[];\r
471\r
472if (unitno) return SCPE_ARG; /* only unit 0 */\r
473M[0] = 077777771; /* -7B */\r
474M[1] = 007100000; /* LDX 0 */\r
475M[2] = 000203610; /* EOM 3610B */\r
476M[3] = 003200002; /* WIM 2 */\r
477M[4] = 000100002; /* BRU 2 */\r
478P = 1; /* start at 1 */\r
479return SCPE_OK;\r
480}\r