1 /* pdp18b_mt.c: 18b PDP 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.
26 mt (PDP-9) TC59 magtape
27 (PDP-15) TC59D magtape
29 16-Feb-06 RMS Added tape capacity checking
30 16-Aug-05 RMS Fixed C++ declaration and cast problems
31 18-Mar-05 RMS Added attached test to detach routine
32 14-Jan-04 RMS Revised IO device call interface
33 25-Apr-03 RMS Revised for extended file support
34 28-Mar-03 RMS Added multiformat support
35 04-Mar-03 RMS Fixed bug in MTTR
36 01-Mar-03 RMS Fixed bug in interrupt handling
37 Revised for magtape library
38 02-Feb-03 RMS Revised IOT decoding
39 30-Oct-02 RMS Revised BOT handling, added error record handling
40 05-Oct-02 RMS Added DIB, device number support
41 Revamped error recovery
42 28-Aug-02 RMS Added end of medium support
43 30-May-02 RMS Widened POS to 32b
44 22-Apr-02 RMS Added maximum record length test
45 06-Jan-02 RMS Revised enabled/disable support
46 29-Nov-01 RMS Added read only unit support
47 25-Nov-01 RMS Revised interrupt structure
48 Changed UST, POS, FLG to arrays
49 26-Apr-01 RMS Added device enable/disable support
50 15-Feb-01 RMS Fixed 3-cycle data break sequence
51 04-Oct-98 RMS V2.4 magtape format
52 22-Jan-97 RMS V2.3 magtape format
53 29-Jun-96 RMS Added unit enable/disable support
55 Magnetic tapes are represented as a series of variable records
66 If the byte count is odd, the record is padded with an extra byte
67 of junk. File marks are represented by a byte count of 0.
70 #include "pdp18b_defs.h"
73 #define MT_NUMDR 8 /* #drives */
74 #define USTAT u3 /* unit status */
75 #define MT_MAXFR (1 << 16) /* max record length */
76 #define MT_WC 032 /* word count */
77 #define MT_CA 033 /* current addr */
78 #define WC_SIZE (1 << 12) /* max word count */
79 #define WC_MASK (WC_SIZE - 1)
81 /* Command/unit - mt_cu */
83 #define CU_V_UNIT 15 /* unit */
85 #define CU_PARITY 0040000 /* parity select */
86 #define CU_DUMP 0020000 /* dump mode */
87 #define CU_ERASE 0010000 /* ext rec gap */
88 #define CU_V_CMD 9 /* command */
98 #define CU_IE 0000400 /* interrupt enable */
99 #define CU_V_TYPE 6 /* drive type */
102 #define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT)
103 #define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD)
104 #define GET_TYPE(x) (((x) >> CU_V_TYPE) & CU_M_TYPE)
105 #define PACKED(x) (((x) & CU_DUMP) || (GET_TYPE (x) != TY_9TK))
107 /* Status - stored in mt_sta or (*) uptr->USTAT */
109 #define STA_ERR 0400000 /* error */
110 #define STA_REW 0200000 /* *rewinding */
111 #define STA_BOT 0100000 /* *start of tape */
112 #define STA_ILL 0040000 /* illegal cmd */
113 #define STA_PAR 0020000 /* parity error */
114 #define STA_EOF 0010000 /* *end of file */
115 #define STA_EOT 0004000 /* *end of tape */
116 #define STA_CPE 0002000 /* compare error */
117 #define STA_RLE 0001000 /* rec lnt error */
118 #define STA_DLT 0000400 /* data late */
119 #define STA_BAD 0000200 /* bad tape */
120 #define STA_DON 0000100 /* done */
122 #define STA_CLR 0000077 /* always clear */
123 #define STA_DYN (STA_REW | STA_BOT | STA_EOF | STA_EOT)
127 extern int32 int_hwre
[API_HLVL
+1];
128 extern UNIT cpu_unit
;
130 int32 mt_cu
= 0; /* command/unit */
131 int32 mt_sta
= 0; /* status register */
132 int32 mt_time
= 10; /* record latency */
133 int32 mt_stopioe
= 1; /* stop on error */
135 uint8
*mtxb
= NULL
; /* transfer buffer */
138 int32
mt (int32 dev
, int32 pulse
, int32 dat
);
139 int32
mt_iors (void);
140 t_stat
mt_svc (UNIT
*uptr
);
141 t_stat
mt_reset (DEVICE
*dptr
);
142 t_stat
mt_attach (UNIT
*uptr
, char *cptr
);
143 t_stat
mt_detach (UNIT
*uptr
);
144 int32
mt_updcsta (UNIT
*uptr
, int32 val
);
145 t_stat
mt_map_err (UNIT
*uptr
, t_stat st
);
146 UNIT
*mt_busy (void);
148 /* MT data structures
150 mt_dev MT device descriptor
152 mt_reg MT register list
153 mt_mod MT modifier list
156 DIB mt_dib
= { DEV_MT
, 1, &mt_iors
, { &mt
} };
159 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
160 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
161 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
162 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
163 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
164 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
165 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) },
166 { UDATA (&mt_svc
, UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_ROABLE
, 0) }
170 { ORDATA (STA
, mt_sta
, 18) },
171 { ORDATA (CMD
, mt_cu
, 18) },
172 { ORDATA (WC
, M
[MT_WC
], 18) },
173 { ORDATA (CA
, M
[MT_CA
], 18) },
174 { FLDATA (INT
, int_hwre
[API_MTA
], INT_V_MTA
) },
175 { FLDATA (STOP_IOE
, mt_stopioe
, 0) },
176 { DRDATA (TIME
, mt_time
, 24), PV_LEFT
},
177 { URDATA (UST
, mt_unit
[0].USTAT
, 8, 16, 0, MT_NUMDR
, 0) },
178 { URDATA (POS
, mt_unit
[0].pos
, 10, T_ADDR_W
, 0,
179 MT_NUMDR
, PV_LEFT
| REG_RO
) },
180 { FLDATA (LOG
, mt_log
, 0), REG_HIDDEN
},
181 { ORDATA (DEVNO
, mt_dib
.dev
, 6), REG_HRO
},
186 { MTUF_WLK
, 0, "write enabled", "WRITEENABLED", NULL
},
187 { MTUF_WLK
, MTUF_WLK
, "write locked", "LOCKED", NULL
},
188 { MTAB_XTD
|MTAB_VUN
, 0, "FORMAT", "FORMAT",
189 &sim_tape_set_fmt
, &sim_tape_show_fmt
, NULL
},
190 { MTAB_XTD
|MTAB_VUN
, 0, "CAPACITY", "CAPACITY",
191 &sim_tape_set_capac
, &sim_tape_show_capac
, NULL
},
192 { MTAB_XTD
|MTAB_VDV
, 0, "DEVNO", "DEVNO",
193 &set_devno
, &show_devno
, NULL
},
198 "MT", mt_unit
, mt_reg
, mt_mod
,
199 MT_NUMDR
, 10, 31, 1, 8, 8,
200 NULL
, NULL
, &mt_reset
,
201 NULL
, &mt_attach
, &mt_detach
,
207 int32
mt (int32 dev
, int32 pulse
, int32 dat
)
212 uptr
= mt_dev
.units
+ GET_UNIT (mt_cu
); /* get unit */
213 mt_updcsta (uptr
, 0); /* update status */
214 sb
= pulse
& 060; /* subop */
216 if ((sb
== 000) && (uptr
->flags
& UNIT_ATT
) && /* MTTR */
217 !sim_is_active (uptr
))
219 else if ((sb
== 020) && !mt_busy ()) /* MTCR */
221 else if ((sb
== 040) && (mt_sta
& (STA_ERR
| STA_DON
))) /* MTSF */
224 if ((pulse
& 06) && mt_log
)
225 printf ("[MT%d: IOT=%o, AC=%o, sta=%o]\n",
226 GET_UNIT (mt_cu
), 0707300 + pulse
, dat
, mt_sta
);
228 if (sb
== 000) dat
= dat
| (mt_cu
& 0777700); /* MTRC */
229 else if (sb
== 020) { /* MTAF, MTLC */
230 if (!mt_busy ()) mt_cu
= mt_sta
= 0; /* if not busy, clr */
231 mt_sta
= mt_sta
& ~(STA_ERR
| STA_DON
); /* clear flags */
233 else if (sb
== 040) dat
= dat
| mt_sta
; /* MTRS */
236 if (sb
== 000) { /* MTGO */
237 f
= GET_CMD (mt_cu
); /* get function */
239 sim_is_active (uptr
) ||
241 (((f
== FN_SPACER
) || (f
== FN_REWIND
)) && (uptr
->USTAT
& STA_BOT
)) ||
242 (((f
== FN_WRITE
) || (f
== FN_WREOF
)) && sim_tape_wrp (uptr
)) ||
243 ((uptr
->flags
& UNIT_ATT
) == 0))
244 mt_sta
= mt_sta
| STA_ILL
| STA_ERR
; /* set illegal op */
246 if (f
== FN_REWIND
) uptr
->USTAT
= STA_REW
; /* rewind? */
247 else mt_sta
= uptr
->USTAT
= 0; /* no, clear status */
248 sim_activate (uptr
, mt_time
); /* start io */
251 if (sb
== 020) /* MTCM, MTLC */
252 mt_cu
= (mt_cu
& 0770700) | (dat
& 0777700); /* load status */
254 mt_updcsta (mt_dev
.units
+ GET_UNIT (mt_cu
), 0); /* update status */
260 If rewind done, reposition to start of tape, set status
261 else, do operation, set done, interrupt
264 t_stat
mt_svc (UNIT
*uptr
)
266 int32 c
, c1
, c2
, c3
, f
, i
, p
, u
;
270 t_stat st
, r
= SCPE_OK
;
272 u
= (int32
) (uptr
- mt_dev
.units
); /* get unit number */
273 f
= GET_CMD (mt_cu
); /* get command */
274 wc
= WC_SIZE
- (M
[MT_WC
] & WC_MASK
); /* word count is 12b */
276 if (uptr
->USTAT
& STA_REW
) { /* rewind? */
277 sim_tape_rewind (uptr
); /* rewind tape */
278 if (uptr
->flags
& UNIT_ATT
) uptr
->USTAT
= STA_BOT
;
279 else uptr
->USTAT
= 0;
280 if (u
== GET_UNIT (mt_cu
)) mt_updcsta (uptr
, STA_DON
);
281 if (mt_log
) printf ("[MT%d: rewind complete, sta=%o]\n", u
, mt_sta
);
285 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* if not attached */
286 mt_updcsta (uptr
, STA_ILL
); /* illegal operation */
287 return IORETURN (mt_stopioe
, SCPE_UNATT
);
290 passed_eot
= sim_tape_eot (uptr
); /* passed EOT? */
291 switch (f
) { /* case on function */
293 case FN_READ
: /* read */
294 case FN_CMPARE
: /* read/compare */
295 st
= sim_tape_rdrecf (uptr
, mtxb
, &tbc
, MT_MAXFR
); /* read rec */
296 if (st
== MTSE_RECE
) mt_sta
= mt_sta
| STA_PAR
| STA_ERR
; /* rec in err? */
297 else if (st
!= MTSE_OK
) { /* other error? */
298 mt_sta
= mt_sta
| STA_RLE
| STA_ERR
; /* set RLE flag */
299 r
= mt_map_err (uptr
, st
); /* map error */
302 cbc
= PACKED (mt_cu
)? wc
* 3: wc
* 2; /* expected bc */
303 if (tbc
!= cbc
) mt_sta
= mt_sta
| STA_RLE
| STA_ERR
; /* wrong size? */
304 if (tbc
< cbc
) { /* record small? */
305 cbc
= tbc
; /* use smaller */
306 wc
= PACKED (mt_cu
)? ((tbc
+ 2) / 3): ((tbc
+ 1) / 2);
308 for (i
= p
= 0; i
< wc
; i
++) { /* copy buffer */
309 M
[MT_WC
] = (M
[MT_WC
] + 1) & DMASK
; /* inc WC, CA */
310 M
[MT_CA
] = (M
[MT_CA
] + 1) & DMASK
;
311 xma
= M
[MT_CA
] & AMASK
;
312 if (PACKED (mt_cu
)) { /* packed? */
313 c1
= mtxb
[p
++] & 077;
314 c2
= mtxb
[p
++] & 077;
315 c3
= mtxb
[p
++] & 077;
316 c
= (c1
<< 12) | (c2
<< 6) | c3
;
323 if ((f
== FN_READ
) && MEM_ADDR_OK (xma
)) M
[xma
] = c
;
324 else if ((f
== FN_CMPARE
) && (c
!= (M
[xma
] &
325 (PACKED (mt_cu
)? DMASK
: 0177777)))) {
326 mt_updcsta (uptr
, STA_CPE
);
332 case FN_WRITE
: /* write */
333 tbc
= PACKED (mt_cu
)? wc
* 3: wc
* 2;
334 xma
= M
[MT_CA
] & AMASK
; /* get mem addr */
335 for (i
= p
= 0; i
< wc
; i
++) { /* copy buf to tape */
336 xma
= (xma
+ 1) & AMASK
; /* incr mem addr */
337 if (PACKED (mt_cu
)) { /* packed? */
338 mtxb
[p
++] = (M
[xma
] >> 12) & 077;
339 mtxb
[p
++] = (M
[xma
] >> 6) & 077;
340 mtxb
[p
++] = M
[xma
] & 077;
343 mtxb
[p
++] = (M
[xma
] >> 8) & 0377;
344 mtxb
[p
++] = M
[xma
] & 0377;
347 if (st
= sim_tape_wrrecf (uptr
, mtxb
, tbc
)) /* write rec, err? */
348 r
= mt_map_err (uptr
, st
); /* map error */
350 M
[MT_CA
] = (M
[MT_CA
] + wc
) & DMASK
; /* advance mem addr */
351 M
[MT_WC
] = 0; /* clear word cnt */
353 mt_cu
= mt_cu
& ~CU_ERASE
; /* clear erase flag */
357 if (st
= sim_tape_wrtmk (uptr
)) /* write tmk, err? */
358 r
= mt_map_err (uptr
, st
); /* map error */
359 else uptr
->USTAT
= STA_EOF
;
360 mt_cu
= mt_cu
& ~CU_ERASE
; /* clear erase flag */
363 case FN_SPACEF
: /* space forward */
365 M
[MT_WC
] = (M
[MT_WC
] + 1) & DMASK
; /* inc WC */
366 if (st
= sim_tape_sprecf (uptr
, &tbc
)) { /* space rec fwd, err? */
367 r
= mt_map_err (uptr
, st
); /* map error */
370 } while ((M
[MT_WC
] != 0) && (passed_eot
|| !sim_tape_eot (uptr
)));
373 case FN_SPACER
: /* space reverse */
375 M
[MT_WC
] = (M
[MT_WC
] + 1) & DMASK
; /* inc WC */
376 if (st
= sim_tape_sprecr (uptr
, &tbc
)) { /* space rec rev, err? */
377 r
= mt_map_err (uptr
, st
); /* map error */
380 } while (M
[MT_WC
] != 0);
384 if (!passed_eot
&& sim_tape_eot (uptr
)) /* just passed EOT? */
385 uptr
->USTAT
= uptr
->USTAT
| STA_EOT
;
386 mt_updcsta (uptr
, STA_DON
); /* set done */
387 if (mt_log
) printf ("MT%d: fnc=%d done, ma=%o, wc=%o, sta=%o]\n",
388 u
, f
, M
[MT_CA
], M
[MT_WC
], mt_sta
);
392 /* Update controller status */
394 int32
mt_updcsta (UNIT
*uptr
, int32 news
)
396 mt_sta
= (mt_sta
& ~(STA_DYN
| STA_CLR
)) |
397 (uptr
->USTAT
& STA_DYN
) | news
;
398 if ((mt_sta
& (STA_ERR
| STA_DON
)) && (mt_cu
& CU_IE
))
400 else CLR_INT (MTA
); /* int request */
404 /* Test if controller busy */
411 for (u
= 0; u
< MT_NUMDR
; u
++) { /* loop thru units */
412 uptr
= mt_dev
.units
+ u
;
413 if (sim_is_active (uptr
) && ((uptr
->USTAT
& STA_REW
) == 0))
419 /* Map tape error status */
421 t_stat
mt_map_err (UNIT
*uptr
, t_stat st
)
425 case MTSE_FMT
: /* illegal fmt */
426 case MTSE_UNATT
: /* not attached */
427 mt_sta
= mt_sta
| STA_ILL
| STA_ERR
;
428 case MTSE_OK
: /* no error */
431 case MTSE_TMK
: /* end of file */
432 uptr
->USTAT
= uptr
->USTAT
| STA_EOF
; /* set EOF */
433 mt_sta
= mt_sta
| STA_ERR
;
436 case MTSE_IOERR
: /* IO error */
437 mt_sta
= mt_sta
| STA_PAR
| STA_ERR
; /* set par err */
438 if (mt_stopioe
) return SCPE_IOERR
;
441 case MTSE_INVRL
: /* invalid rec lnt */
442 mt_sta
= mt_sta
| STA_PAR
| STA_ERR
; /* set par err */
445 case MTSE_RECE
: /* record in error */
446 mt_sta
= mt_sta
| STA_PAR
| STA_ERR
; /* set par err */
449 case MTSE_EOM
: /* end of medium */
450 mt_sta
= mt_sta
| STA_BAD
| STA_ERR
; /* set end tape */
453 case MTSE_BOT
: /* reverse into BOT */
454 uptr
->USTAT
= uptr
->USTAT
| STA_BOT
; /* set status */
455 mt_sta
= mt_sta
| STA_ERR
;
458 case MTSE_WRP
: /* write protect */
459 mt_sta
= mt_sta
| STA_ILL
| STA_ERR
; /* illegal operation */
468 t_stat
mt_reset (DEVICE
*dptr
)
474 for (u
= 0; u
< MT_NUMDR
; u
++) { /* loop thru units */
475 uptr
= mt_dev
.units
+ u
;
476 sim_tape_reset (uptr
); /* reset tape */
477 sim_cancel (uptr
); /* cancel activity */
478 if (uptr
->flags
& UNIT_ATT
) uptr
->USTAT
= STA_BOT
;
479 else uptr
->USTAT
= 0;
481 mt_updcsta (&mt_unit
[0], 0); /* update status */
482 if (mtxb
== NULL
) mtxb
= (uint8
*) calloc (MT_MAXFR
, sizeof (uint8
));
483 if (mtxb
== NULL
) return SCPE_MEM
;
491 return (mt_sta
& (STA_ERR
| STA_DON
))? IOS_MTA
: 0;
496 t_stat
mt_attach (UNIT
*uptr
, char *cptr
)
500 r
= sim_tape_attach (uptr
, cptr
);
501 if (r
!= SCPE_OK
) return r
;
502 uptr
->USTAT
= STA_BOT
;
503 mt_updcsta (mt_dev
.units
+ GET_UNIT (mt_cu
), 0); /* update status */
509 t_stat
mt_detach (UNIT
* uptr
)
511 if (!(uptr
->flags
& UNIT_ATT
)) return SCPE_OK
; /* attached? */
512 if (!sim_is_active (uptr
)) uptr
->USTAT
= 0;
513 mt_updcsta (mt_dev
.units
+ GET_UNIT (mt_cu
), 0); /* update status */
514 return sim_tape_detach (uptr
);