First Commit of my working state
[simh.git] / HP2100 / hp2100_mt.c
1 /* hp2100_mt.c: HP 2100 12559A magnetic tape simulator
2
3 Copyright (c) 1993-2006, 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 12559A 3030 nine track magnetic tape
27
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
47
48 Magnetic tapes are represented as a series of variable records
49 of the form:
50
51 32b byte count
52 byte 0
53 byte 1
54 :
55 byte n-2
56 byte n-1
57 32b byte count
58
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.
61
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.
64 */
65
66 #include "hp2100_defs.h"
67 #include "sim_tape.h"
68
69 #define DB_V_SIZE 16 /* max data buf */
70 #define DBSIZE (1 << DB_V_SIZE) /* max data cmd */
71
72 /* Command - mtc_fnc */
73
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 */
83
84 /* Status - stored in mtc_sta, (d) = dynamic */
85
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) */
95
96 extern uint32 PC;
97 extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2];
98
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 };
111
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);
121
122 /* MTD data structures
123
124 mtd_dev MTD device descriptor
125 mtd_unit MTD unit list
126 mtd_reg MTD register list
127 */
128
129 DIB mt_dib[] = {
130 { MTD, 0, 0, 0, 0, 0, &mtdio },
131 { MTC, 0, 0, 0, 0, 0, &mtcio }
132 };
133
134 #define mtd_dib mt_dib[0]
135 #define mtc_dib mt_dib[1]
136
137 UNIT mtd_unit = { UDATA (NULL, 0, 0) };
138
139 REG mtd_reg[] = {
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 },
149 { NULL }
150 };
151
152 MTAB mtd_mod[] = {
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 },
159 { 0 }
160 };
161
162 DEVICE mtd_dev = {
163 "MTD", &mtd_unit, mtd_reg, mtd_mod,
164 1, 10, 16, 1, 8, 8,
165 NULL, NULL, &mtd_reset,
166 NULL, NULL, NULL,
167 &mtd_dib, DEV_DISABLE | DEV_DIS
168 };
169
170 /* MTC data structures
171
172 mtc_dev MTC device descriptor
173 mtc_unit MTC unit list
174 mtc_reg MTC register list
175 mtc_mod MTC modifier list
176 */
177
178 UNIT mtc_unit = { UDATA (&mtc_svc, UNIT_ATTABLE + UNIT_ROABLE, 0) };
179
180 REG mtc_reg[] = {
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 },
197 { NULL }
198 };
199
200 MTAB mtc_mod[] = {
201 { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO",
202 &hp_setdev, &hp_showdev, &mtd_dev },
203 { 0 }
204 };
205
206 DEVICE mtc_dev = {
207 "MTC", &mtc_unit, mtc_reg, mtc_mod,
208 1, 10, 31, 1, 8, 8,
209 NULL, NULL, &mtc_reset,
210 NULL, &mtc_attach, &mtc_detach,
211 &mtc_dib, DEV_DISABLE | DEV_DIS
212 };
213
214 /* IO instructions */
215
216 int32 mtdio (int32 inst, int32 IR, int32 dat)
217 {
218 int32 devd;
219
220 devd = IR & I_DEVMASK; /* get device no */
221 switch (inst) { /* case on opcode */
222
223 case ioFLG: /* flag clear/set */
224 if ((IR & I_HC) == 0) { setFSR (devd); } /* STF */
225 break;
226
227 case ioSFC: /* skip flag clear */
228 if (FLG (devd) == 0) PC = (PC + 1) & VAMASK;
229 break;
230
231 case ioSFS: /* skip flag set */
232 if (FLG (devd) != 0) PC = (PC + 1) & VAMASK;
233 break;
234
235 case ioOTX: /* output */
236 mtc_unit.buf = dat & 0377; /* store data */
237 break;
238
239 case ioMIX: /* merge */
240 dat = dat | mtc_unit.buf;
241 break;
242
243 case ioLIX: /* load */
244 dat = mtc_unit.buf;
245 break;
246
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 */
250 break;
251
252 default:
253 break;
254 }
255
256 if (IR & I_HC) { clrFSR (devd); } /* H/C option */
257 return dat;
258 }
259
260 int32 mtcio (int32 inst, int32 IR, int32 dat)
261 {
262 uint32 i;
263 int32 devc, devd, valid;
264 t_stat st;
265
266 devc = IR & I_DEVMASK; /* get device no */
267 devd = devc - 1;
268 switch (inst) { /* case on opcode */
269
270 case ioFLG: /* flag clear/set */
271 if ((IR & I_HC) == 0) { setFSR (devc); } /* STF */
272 break;
273
274 case ioSFC: /* skip flag clear */
275 if (FLG (devc) == 0) PC = (PC + 1) & VAMASK;
276 break;
277
278 case ioSFS: /* skip flag set */
279 if (FLG (devc) != 0) PC = (PC + 1) & VAMASK;
280 break;
281
282 case ioOTX: /* output */
283 dat = dat & 0377;
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);
290 }
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 */
296 clrFSR (devc);
297 clrCTL (devd);
298 clrFSR (devd);
299 return SCPE_OK;
300 }
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;
308 else {
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 */
314 clrFSR (devd);
315 mtc_1st = 1; /* set 1st flop */
316 mtc_dtf = 1; /* set xfer flop */
317 }
318 break;
319
320 case ioLIX: /* load */
321 dat = 0;
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;
327 }
328 else dat = dat | STA_BUSY | STA_LOCAL;
329 break;
330
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 */
335 break;
336
337 default:
338 break;
339 }
340
341 if (IR & I_HC) { clrFSR (devc); } /* H/C option */
342 return dat;
343 }
344
345 /* Unit service
346
347 If rewind done, reposition to start of tape, set status
348 else, do operation, set done, interrupt
349
350 Can't be write locked, can only write lock detached unit
351 */
352
353 t_stat mtc_svc (UNIT *uptr)
354 {
355 int32 devc, devd;
356 t_mtrlnt tbc;
357 t_stat st, r = SCPE_OK;
358
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);
365 }
366
367 switch (mtc_fnc) { /* case on function */
368
369 case FNC_REW: /* rewind */
370 sim_tape_rewind (uptr); /* BOT */
371 mtc_sta = STA_BOT; /* update status */
372 break;
373
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 */
377
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 */
382 break;
383
384 case FNC_GAP: /* erase gap */
385 break;
386
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 */
390 break;
391
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 */
395 break;
396
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 */
407 return SCPE_OK;
408 }
409 break; /* non-recov, done */
410 }
411 if (mt_max < 12) { /* record too short? */
412 mtc_sta = mtc_sta | STA_PAR; /* set flag */
413 break;
414 }
415 }
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 */
421 return SCPE_OK;
422 }
423 sim_activate (uptr, mtc_gtime); /* schedule gap */
424 mtc_fnc = 0; /* nop */
425 return SCPE_OK;
426
427 case FNC_WC: /* write */
428 if (mtc_1st) mtc_1st = 0; /* no xfr on first */
429 else {
430 if (mt_ptr < DBSIZE) { /* room in buffer? */
431 mtxb[mt_ptr++] = mtc_unit.buf;
432 mtc_sta = mtc_sta & ~STA_BOT; /* clear BOT */
433 }
434 else mtc_sta = mtc_sta | STA_PAR;
435 }
436 if (mtc_dtf) { /* xfer flop set? */
437 setFSR (devd); /* set dch flag */
438 sim_activate (uptr, mtc_xtime); /* re-activate */
439 return SCPE_OK;
440 }
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 */
444 break; /* done */
445 }
446 }
447 sim_activate (uptr, mtc_gtime); /* schedule gap */
448 mtc_fnc = 0; /* nop */
449 return SCPE_OK;
450
451 default: /* unknown */
452 break;
453 }
454
455 setFSR (devc); /* set cch flg */
456 mtc_sta = mtc_sta & ~STA_BUSY; /* not busy */
457 return r;
458 }
459
460 /* Map tape error status */
461
462 t_stat mt_map_err (UNIT *uptr, t_stat st)
463 {
464 switch (st) {
465
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! */
471
472 case MTSE_EOM: /* end of medium */
473 case MTSE_TMK: /* end of file */
474 mtc_sta = mtc_sta | STA_EOF; /* eof */
475 break;
476
477 case MTSE_IOERR: /* IO error */
478 mtc_sta = mtc_sta | STA_PAR; /* error */
479 if (mtc_stopioe) return SCPE_IOERR;
480 break;
481
482 case MTSE_INVRL: /* invalid rec lnt */
483 mtc_sta = mtc_sta | STA_PAR;
484 return SCPE_MTRLNT;
485
486 case MTSE_RECE: /* record in error */
487 mtc_sta = mtc_sta | STA_PAR; /* error */
488 break;
489
490 case MTSE_BOT: /* reverse into BOT */
491 mtc_sta = mtc_sta | STA_BOT; /* set status */
492 break;
493
494 case MTSE_WRP: /* write protect */
495 mtc_sta = mtc_sta | STA_REJ; /* reject */
496 break;
497 }
498
499 return SCPE_OK;
500 }
501
502 /* Reset routine */
503
504 t_stat mtd_reset (DEVICE *dptr)
505 {
506 hp_enbdis_pair (&mtd_dev, &mtc_dev); /* make pair cons */
507 return mtc_reset (dptr); /* do common reset */
508 }
509
510 t_stat mtc_reset (DEVICE *dptr)
511 {
512 hp_enbdis_pair (&mtc_dev, &mtd_dev); /* make pair cons */
513 mtc_fnc = 0;
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;
526 return SCPE_OK;
527 }
528
529 /* Attach routine */
530
531 t_stat mtc_attach (UNIT *uptr, char *cptr)
532 {
533 t_stat r;
534
535 r = sim_tape_attach (uptr, cptr); /* attach unit */
536 if (r != SCPE_OK) return r; /* update status */
537 mtc_sta = STA_BOT;
538 return r;
539 }
540
541 /* Detach routine */
542
543 t_stat mtc_detach (UNIT* uptr)
544 {
545 mtc_sta = 0; /* update status */
546 return sim_tape_detach (uptr); /* detach unit */
547 }