61f379eec5e4195252614b78933d0d686e332158
1 /* hp2100_mt.c: HP 2100 12559A 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 12559A 3030 nine track magnetic tape
28 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified)
29 07-Oct-04 JDB Allow enable/disable from either device
30 14-Aug-04 RMS Modified handling of end of medium (suggested by Dave Bryan)
31 06-Jul-04 RMS Fixed spurious timing error after CLC (found by Dave Bryan)
32 26-Apr-04 RMS Fixed SFS x,C and SFC x,C
33 Implemented DMA SRQ (follows FLG)
34 21-Dec-03 RMS Adjusted msc_ctime for TSB (from Mike Gemeny)
35 25-Apr-03 RMS Revised for extended file support
36 28-Mar-03 RMS Added multiformat support
37 28-Feb-03 RMS Revised for magtape library
38 30-Sep-02 RMS Revamped error handling
39 28-Aug-02 RMS Added end of medium support
40 30-May-02 RMS Widened POS to 32b
41 22-Apr-02 RMS Added maximum record length test
42 20-Jan-02 RMS Fixed bug on last character write
43 03-Dec-01 RMS Added read only unit, extended SET/SHOW support
44 07-Sep-01 RMS Moved function prototypes
45 30-Nov-00 RMS Made variable names unique
46 04-Oct-98 RMS V2.4 magtape format
48 Magnetic tapes are represented as a series of variable records
59 If the byte count is odd, the record is padded with an extra byte
60 of junk. File marks are represented by a byte count of 0.
62 Unusually among HP peripherals, the 12559 does not have a command flop,
63 and its flag and flag buffer power up as clear rather than set.
66 #include "hp2100_defs.h"
69 #define DB_V_SIZE 16 /* max data buf */
70 #define DBSIZE (1 << DB_V_SIZE) /* max data cmd */
72 /* Command - mtc_fnc */
74 #define FNC_CLR 0300 /* clear */
75 #define FNC_WC 0031 /* write */
76 #define FNC_RC 0023 /* read */
77 #define FNC_GAP 0011 /* write gap */
78 #define FNC_FSR 0003 /* forward space */
79 #define FNC_BSR 0041 /* backward space */
80 #define FNC_REW 0201 /* rewind */
81 #define FNC_RWS 0101 /* rewind and offline */
82 #define FNC_WFM 0035 /* write file mark */
84 /* Status - stored in mtc_sta, (d) = dynamic */
86 #define STA_LOCAL 0400 /* local (d) */
87 #define STA_EOF 0200 /* end of file */
88 #define STA_BOT 0100 /* beginning of tape */
89 #define STA_EOT 0040 /* end of tape */
90 #define STA_TIM 0020 /* timing error */
91 #define STA_REJ 0010 /* programming error */
92 #define STA_WLK 0004 /* write locked (d) */
93 #define STA_PAR 0002 /* parity error */
94 #define STA_BUSY 0001 /* busy (d) */
97 extern uint32 dev_cmd
[2], dev_ctl
[2], dev_flg
[2], dev_fbf
[2], dev_srq
[2];
99 int32 mtc_fnc
= 0; /* function */
100 int32 mtc_sta
= 0; /* status register */
101 int32 mtc_dtf
= 0; /* data xfer flop */
102 int32 mtc_1st
= 0; /* first svc flop */
103 int32 mtc_ctime
= 40; /* command wait */
104 int32 mtc_gtime
= 1000; /* gap stop time */
105 int32 mtc_xtime
= 15; /* data xfer time */
106 int32 mtc_stopioe
= 1; /* stop on error */
107 uint8 mtxb
[DBSIZE
] = { 0 }; /* data buffer */
108 t_mtrlnt mt_ptr
= 0, mt_max
= 0; /* buffer ptrs */
109 static const int32 mtc_cmd
[] = {
110 FNC_WC
, FNC_RC
, FNC_GAP
, FNC_FSR
, FNC_BSR
, FNC_REW
, FNC_RWS
, FNC_WFM
};
112 DEVICE mtd_dev
, mtc_dev
;
113 int32
mtdio (int32 inst
, int32 IR
, int32 dat
);
114 int32
mtcio (int32 inst
, int32 IR
, int32 dat
);
115 t_stat
mtc_svc (UNIT
*uptr
);
116 t_stat
mtd_reset (DEVICE
*dptr
);
117 t_stat
mtc_reset (DEVICE
*dptr
);
118 t_stat
mtc_attach (UNIT
*uptr
, char *cptr
);
119 t_stat
mtc_detach (UNIT
*uptr
);
120 t_stat
mt_map_err (UNIT
*uptr
, t_stat st
);
122 /* MTD data structures
124 mtd_dev MTD device descriptor
125 mtd_unit MTD unit list
126 mtd_reg MTD register list
130 { MTD
, 0, 0, 0, 0, 0, &mtdio
},
131 { MTC
, 0, 0, 0, 0, 0, &mtcio
}
134 #define mtd_dib mt_dib[0]
135 #define mtc_dib mt_dib[1]
137 UNIT mtd_unit
= { UDATA (NULL
, 0, 0) };
140 { FLDATA (CMD
, mtd_dib
.cmd
, 0), REG_HRO
},
141 { FLDATA (CTL
, mtd_dib
.ctl
, 0), REG_HRO
},
142 { FLDATA (FLG
, mtd_dib
.flg
, 0) },
143 { FLDATA (FBF
, mtd_dib
.fbf
, 0) },
144 { FLDATA (SRQ
, mtd_dib
.srq
, 0) },
145 { BRDATA (DBUF
, mtxb
, 8, 8, DBSIZE
) },
146 { DRDATA (BPTR
, mt_ptr
, DB_V_SIZE
+ 1) },
147 { DRDATA (BMAX
, mt_max
, DB_V_SIZE
+ 1) },
148 { ORDATA (DEVNO
, mtd_dib
.devno
, 6), REG_HRO
},
153 { MTUF_WLK
, 0, "write enabled", "WRITEENABLED", NULL
},
154 { MTUF_WLK
, MTUF_WLK
, "write locked", "LOCKED", NULL
},
155 { MTAB_XTD
|MTAB_VUN
, 0, "FORMAT", "FORMAT",
156 &sim_tape_set_fmt
, &sim_tape_show_fmt
, NULL
},
157 { MTAB_XTD
| MTAB_VDV
, 1, "DEVNO", "DEVNO",
158 &hp_setdev
, &hp_showdev
, &mtd_dev
},
163 "MTD", &mtd_unit
, mtd_reg
, mtd_mod
,
165 NULL
, NULL
, &mtd_reset
,
167 &mtd_dib
, DEV_DISABLE
| DEV_DIS
170 /* MTC data structures
172 mtc_dev MTC device descriptor
173 mtc_unit MTC unit list
174 mtc_reg MTC register list
175 mtc_mod MTC modifier list
178 UNIT mtc_unit
= { UDATA (&mtc_svc
, UNIT_ATTABLE
+ UNIT_ROABLE
, 0) };
181 { ORDATA (FNC
, mtc_fnc
, 8) },
182 { ORDATA (STA
, mtc_sta
, 9) },
183 { ORDATA (BUF
, mtc_unit
.buf
, 8) },
184 { FLDATA (CMD
, mtc_dib
.cmd
, 0), REG_HRO
},
185 { FLDATA (CTL
, mtc_dib
.ctl
, 0) },
186 { FLDATA (FLG
, mtc_dib
.flg
, 0) },
187 { FLDATA (FBF
, mtc_dib
.fbf
, 0) },
188 { FLDATA (SRQ
, mtc_dib
.srq
, 0) },
189 { FLDATA (DTF
, mtc_dtf
, 0) },
190 { FLDATA (FSVC
, mtc_1st
, 0) },
191 { DRDATA (POS
, mtc_unit
.pos
, T_ADDR_W
), PV_LEFT
},
192 { DRDATA (CTIME
, mtc_ctime
, 24), REG_NZ
+ PV_LEFT
},
193 { DRDATA (GTIME
, mtc_gtime
, 24), REG_NZ
+ PV_LEFT
},
194 { DRDATA (XTIME
, mtc_xtime
, 24), REG_NZ
+ PV_LEFT
},
195 { FLDATA (STOP_IOE
, mtc_stopioe
, 0) },
196 { ORDATA (DEVNO
, mtc_dib
.devno
, 6), REG_HRO
},
201 { MTAB_XTD
| MTAB_VDV
, 1, "DEVNO", "DEVNO",
202 &hp_setdev
, &hp_showdev
, &mtd_dev
},
207 "MTC", &mtc_unit
, mtc_reg
, mtc_mod
,
209 NULL
, NULL
, &mtc_reset
,
210 NULL
, &mtc_attach
, &mtc_detach
,
211 &mtc_dib
, DEV_DISABLE
| DEV_DIS
214 /* IO instructions */
216 int32
mtdio (int32 inst
, int32 IR
, int32 dat
)
220 devd
= IR
& I_DEVMASK
; /* get device no */
221 switch (inst
) { /* case on opcode */
223 case ioFLG
: /* flag clear/set */
224 if ((IR
& I_HC
) == 0) { setFSR (devd
); } /* STF */
227 case ioSFC
: /* skip flag clear */
228 if (FLG (devd
) == 0) PC
= (PC
+ 1) & VAMASK
;
231 case ioSFS
: /* skip flag set */
232 if (FLG (devd
) != 0) PC
= (PC
+ 1) & VAMASK
;
235 case ioOTX
: /* output */
236 mtc_unit
.buf
= dat
& 0377; /* store data */
239 case ioMIX
: /* merge */
240 dat
= dat
| mtc_unit
.buf
;
243 case ioLIX
: /* load */
247 case ioCRS
: /* control reset (action unverif) */
248 case ioCTL
: /* control clear/set */
249 if (IR
& I_CTL
) mtc_dtf
= 0; /* CLC: clr xfer flop */
256 if (IR
& I_HC
) { clrFSR (devd
); } /* H/C option */
260 int32
mtcio (int32 inst
, int32 IR
, int32 dat
)
263 int32 devc
, devd
, valid
;
266 devc
= IR
& I_DEVMASK
; /* get device no */
268 switch (inst
) { /* case on opcode */
270 case ioFLG
: /* flag clear/set */
271 if ((IR
& I_HC
) == 0) { setFSR (devc
); } /* STF */
274 case ioSFC
: /* skip flag clear */
275 if (FLG (devc
) == 0) PC
= (PC
+ 1) & VAMASK
;
278 case ioSFS
: /* skip flag set */
279 if (FLG (devc
) != 0) PC
= (PC
+ 1) & VAMASK
;
282 case ioOTX
: /* output */
284 mtc_sta
= mtc_sta
& ~STA_REJ
; /* clear reject */
285 if (dat
== FNC_CLR
) { /* clear? */
286 if (sim_is_active (&mtc_unit
) && /* write in prog? */
287 (mtc_fnc
== FNC_WC
) && (mt_ptr
> 0)) { /* yes, bad rec */
288 if (st
= sim_tape_wrrecf (&mtc_unit
, mtxb
, mt_ptr
| MTR_ERF
))
289 mt_map_err (&mtc_unit
, st
);
291 if (((mtc_fnc
== FNC_REW
) || (mtc_fnc
== FNC_RWS
)) &&
292 sim_is_active (&mtc_unit
)) sim_cancel (&mtc_unit
);
293 mtc_1st
= mtc_dtf
= 0;
294 mtc_sta
= mtc_sta
& STA_BOT
;
295 clrCTL (devc
); /* init device */
301 for (i
= valid
= 0; i
< sizeof (mtc_cmd
); i
++) /* is fnc valid? */
302 if (dat
== mtc_cmd
[i
]) valid
= 1;
303 if (!valid
|| sim_is_active (&mtc_unit
) || /* is cmd valid? */
304 ((mtc_sta
& STA_BOT
) && (dat
== FNC_BSR
)) ||
305 (sim_tape_wrp (&mtc_unit
) &&
306 ((dat
== FNC_WC
) || (dat
== FNC_GAP
) || (dat
== FNC_WFM
))))
307 mtc_sta
= mtc_sta
| STA_REJ
;
309 sim_activate (&mtc_unit
, mtc_ctime
); /* start tape */
310 mtc_fnc
= dat
; /* save function */
311 mtc_sta
= STA_BUSY
; /* unit busy */
312 mt_ptr
= 0; /* init buffer ptr */
313 clrFSR (devc
); /* clear flags */
315 mtc_1st
= 1; /* set 1st flop */
316 mtc_dtf
= 1; /* set xfer flop */
320 case ioLIX
: /* load */
322 case ioMIX
: /* merge */
323 dat
= dat
| (mtc_sta
& ~(STA_LOCAL
| STA_WLK
| STA_BUSY
));
324 if (mtc_unit
.flags
& UNIT_ATT
) { /* construct status */
325 if (sim_is_active (&mtc_unit
)) dat
= dat
| STA_BUSY
;
326 if (sim_tape_wrp (&mtc_unit
)) dat
= dat
| STA_WLK
;
328 else dat
= dat
| STA_BUSY
| STA_LOCAL
;
331 case ioCRS
: /* control reset (action unverif) */
332 case ioCTL
: /* control clear/set */
333 if (IR
& I_CTL
) { clrCTL (devc
); } /* CLC */
334 else { setCTL (devc
); } /* STC */
341 if (IR
& I_HC
) { clrFSR (devc
); } /* H/C option */
347 If rewind done, reposition to start of tape, set status
348 else, do operation, set done, interrupt
350 Can't be write locked, can only write lock detached unit
353 t_stat
mtc_svc (UNIT
*uptr
)
357 t_stat st
, r
= SCPE_OK
;
359 devc
= mtc_dib
.devno
; /* get device nos */
360 devd
= mtd_dib
.devno
;
361 if ((mtc_unit
.flags
& UNIT_ATT
) == 0) { /* offline? */
362 mtc_sta
= STA_LOCAL
| STA_REJ
; /* rejected */
363 setFSR (devc
); /* set cch flg */
364 return IORETURN (mtc_stopioe
, SCPE_UNATT
);
367 switch (mtc_fnc
) { /* case on function */
369 case FNC_REW
: /* rewind */
370 sim_tape_rewind (uptr
); /* BOT */
371 mtc_sta
= STA_BOT
; /* update status */
374 case FNC_RWS
: /* rewind and offline */
375 sim_tape_rewind (uptr
); /* clear position */
376 return sim_tape_detach (uptr
); /* don't set cch flg */
378 case FNC_WFM
: /* write file mark */
379 if (st
= sim_tape_wrtmk (uptr
)) /* write tmk, err? */
380 r
= mt_map_err (uptr
, st
); /* map error */
381 mtc_sta
= STA_EOF
; /* set EOF status */
384 case FNC_GAP
: /* erase gap */
387 case FNC_FSR
: /* space forward */
388 if (st
= sim_tape_sprecf (uptr
, &tbc
)) /* space rec fwd, err? */
389 r
= mt_map_err (uptr
, st
); /* map error */
392 case FNC_BSR
: /* space reverse */
393 if (st
= sim_tape_sprecr (uptr
, &tbc
)) /* space rec rev, err? */
394 r
= mt_map_err (uptr
, st
); /* map error */
397 case FNC_RC
: /* read */
398 if (mtc_1st
) { /* first svc? */
399 mtc_1st
= mt_ptr
= 0; /* clr 1st flop */
400 st
= sim_tape_rdrecf (uptr
, mtxb
, &mt_max
, DBSIZE
); /* read rec */
401 if (st
== MTSE_RECE
) mtc_sta
= mtc_sta
| STA_PAR
; /* rec in err? */
402 else if (st
!= MTSE_OK
) { /* other error? */
403 r
= mt_map_err (uptr
, st
); /* map error */
404 if (r
== SCPE_OK
) { /* recoverable? */
405 sim_activate (uptr
, mtc_gtime
); /* sched IRG */
406 mtc_fnc
= 0; /* NOP func */
409 break; /* non-recov, done */
411 if (mt_max
< 12) { /* record too short? */
412 mtc_sta
= mtc_sta
| STA_PAR
; /* set flag */
416 if (mtc_dtf
&& (mt_ptr
< mt_max
)) { /* more chars? */
417 if (FLG (devd
)) mtc_sta
= mtc_sta
| STA_TIM
;
418 mtc_unit
.buf
= mtxb
[mt_ptr
++]; /* fetch next */
419 setFSR (devd
); /* set dch flg */
420 sim_activate (uptr
, mtc_xtime
); /* re-activate */
423 sim_activate (uptr
, mtc_gtime
); /* schedule gap */
424 mtc_fnc
= 0; /* nop */
427 case FNC_WC
: /* write */
428 if (mtc_1st
) mtc_1st
= 0; /* no xfr on first */
430 if (mt_ptr
< DBSIZE
) { /* room in buffer? */
431 mtxb
[mt_ptr
++] = mtc_unit
.buf
;
432 mtc_sta
= mtc_sta
& ~STA_BOT
; /* clear BOT */
434 else mtc_sta
= mtc_sta
| STA_PAR
;
436 if (mtc_dtf
) { /* xfer flop set? */
437 setFSR (devd
); /* set dch flag */
438 sim_activate (uptr
, mtc_xtime
); /* re-activate */
441 if (mt_ptr
) { /* write buffer */
442 if (st
= sim_tape_wrrecf (uptr
, mtxb
, mt_ptr
)) { /* write, err? */
443 r
= mt_map_err (uptr
, st
); /* map error */
447 sim_activate (uptr
, mtc_gtime
); /* schedule gap */
448 mtc_fnc
= 0; /* nop */
451 default: /* unknown */
455 setFSR (devc
); /* set cch flg */
456 mtc_sta
= mtc_sta
& ~STA_BUSY
; /* not busy */
460 /* Map tape error status */
462 t_stat
mt_map_err (UNIT
*uptr
, t_stat st
)
466 case MTSE_FMT
: /* illegal fmt */
467 case MTSE_UNATT
: /* unattached */
468 mtc_sta
= mtc_sta
| STA_REJ
; /* reject */
469 case MTSE_OK
: /* no error */
470 return SCPE_IERR
; /* never get here! */
472 case MTSE_EOM
: /* end of medium */
473 case MTSE_TMK
: /* end of file */
474 mtc_sta
= mtc_sta
| STA_EOF
; /* eof */
477 case MTSE_IOERR
: /* IO error */
478 mtc_sta
= mtc_sta
| STA_PAR
; /* error */
479 if (mtc_stopioe
) return SCPE_IOERR
;
482 case MTSE_INVRL
: /* invalid rec lnt */
483 mtc_sta
= mtc_sta
| STA_PAR
;
486 case MTSE_RECE
: /* record in error */
487 mtc_sta
= mtc_sta
| STA_PAR
; /* error */
490 case MTSE_BOT
: /* reverse into BOT */
491 mtc_sta
= mtc_sta
| STA_BOT
; /* set status */
494 case MTSE_WRP
: /* write protect */
495 mtc_sta
= mtc_sta
| STA_REJ
; /* reject */
504 t_stat
mtd_reset (DEVICE
*dptr
)
506 hp_enbdis_pair (&mtd_dev
, &mtc_dev
); /* make pair cons */
507 return mtc_reset (dptr
); /* do common reset */
510 t_stat
mtc_reset (DEVICE
*dptr
)
512 hp_enbdis_pair (&mtc_dev
, &mtd_dev
); /* make pair cons */
514 mtc_1st
= mtc_dtf
= 0;
515 mtc_dib
.cmd
= mtd_dib
.cmd
= 0; /* clear cmd */
516 mtc_dib
.ctl
= mtd_dib
.ctl
= 0; /* clear ctl */
517 mtc_dib
.flg
= mtd_dib
.flg
= 0; /* clear flg */
518 mtc_dib
.fbf
= mtd_dib
.fbf
= 0; /* clear fbf */
519 mtc_dib
.srq
= mtd_dib
.srq
= 0; /* srq follows flg */
520 sim_cancel (&mtc_unit
); /* cancel activity */
521 sim_tape_reset (&mtc_unit
);
522 if (mtc_unit
.flags
& UNIT_ATT
) mtc_sta
=
523 (sim_tape_bot (&mtc_unit
)? STA_BOT
: 0) |
524 (sim_tape_wrp (&mtc_unit
)? STA_WLK
: 0);
525 else mtc_sta
= STA_LOCAL
| STA_BUSY
;
531 t_stat
mtc_attach (UNIT
*uptr
, char *cptr
)
535 r
= sim_tape_attach (uptr
, cptr
); /* attach unit */
536 if (r
!= SCPE_OK
) return r
; /* update status */
543 t_stat
mtc_detach (UNIT
* uptr
)
545 mtc_sta
= 0; /* update status */
546 return sim_tape_detach (uptr
); /* detach unit */