1
/* h316_mt.c: H316/516 magnetic tape simulator
3
Copyright (c) 2003-2004, 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
23
be 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 516-4100 seven track magnetic tape
28
Magnetic tapes are represented as a series of variable records
39
If the byte count is odd, the record is padded with an extra byte
40
of junk. File marks are represented by a byte count of 0.
43
#include "h316_defs.h"
46
#define MT_NUMDR 4 /* number of drives */
47
#define DB_N_SIZE 16 /* max data buf */
48
#define DBSIZE (1 << DB_N_SIZE) /* max data cmd */
49
#define FNC u3 /* function */
50
#define UST u4 /* unit status */
51
#define UNIT_WPRT (MTUF_WLK | UNIT_RO) /* write prot */
71
#define FNC_2ND 020 /* second state */
72
#define FNC_NOP (FNC_STOPW|FNC_2ND)
73
#define FNC_EOM 040 /* end of motion */
75
/* Status - unit.UST */
77
#define STA_BOT 0000002 /* beg of tape */
78
#define STA_EOT 0000001 /* end of tape */
80
extern int32 dev_int, dev_enb, chan_req;
81
extern int32 stop_inst;
83
uint32 mt_buf = 0; /* data buffer */
84
uint32 mt_usel = 0; /* unit select */
85
uint32 mt_busy = 0; /* ctlr busy */
86
uint32 mt_mdirq = 0; /* motion done int req */
87
uint32 mt_rdy = 0; /* transfer ready (int) */
88
uint32 mt_err = 0; /* error */
89
uint32 mt_eof = 0; /* end of file */
90
uint32 mt_eor = 0; /* transfer done */
91
uint32 mt_dma = 0; /* DMA/DMC */
92
uint32 mt_xtime = 16; /* transfer time */
93
uint32 mt_ctime = 3000; /* start/stop time */
94
uint32 mt_stopioe = 1; /* stop on I/O error */
95
uint8 mtxb[DBSIZE] = { 0 }; /* data buffer */
96
t_mtrlnt mt_ptr = 0, mt_max = 0; /* buffer ptrs */
98
int32 mtio (int32 inst, int32 fnc, int32 dat, int32 dev);
99
void mt_updint (uint32 rdy, uint32 mdone);
100
t_stat mt_svc (UNIT *uptr);
101
t_stat mt_reset (DEVICE *dptr);
102
t_stat mt_attach (UNIT *uptr, char *cptr);
103
t_stat mt_detach (UNIT *uptr);
104
t_stat mt_map_err (UNIT *uptr, t_stat st);
105
void mt_wrwd (UNIT *uptr, uint32 dat);
107
/* MT data structures
109
mt_dev MT device descriptor
111
mt_reg MT register list
112
mt_mod MT modifier list
115
DIB mt_dib = { MT, IOBUS, MT_NUMDR, &mtio };
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
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) } };
124
{ ORDATA (BUF, mt_buf, 16) },
125
{ ORDATA (USEL, mt_usel, 2) },
126
{ FLDATA (BUSY, mt_busy, 0) },
127
{ FLDATA (RDY, mt_rdy, 0) },
128
{ FLDATA (ERR, mt_err, 0) },
129
{ FLDATA (EOF, mt_eof, 0) },
130
{ FLDATA (EOR, mt_eor, 0) },
131
{ FLDATA (MDIRQ, mt_mdirq, 0) },
132
{ FLDATA (DMA, mt_dma, 0) },
133
{ FLDATA (INTREQ, dev_int, INT_V_MT) },
134
{ FLDATA (ENABLE, dev_enb, INT_V_MT) },
135
{ BRDATA (DBUF, mtxb, 8, 8, DBSIZE) },
136
{ DRDATA (BPTR, mt_ptr, DB_N_SIZE + 1) },
137
{ DRDATA (BMAX, mt_max, DB_N_SIZE + 1) },
138
{ DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT },
139
{ DRDATA (XTIME, mt_xtime, 24), REG_NZ + PV_LEFT },
140
{ URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR, PV_LEFT) },
141
{ URDATA (FNC, mt_unit[0].FNC, 8, 8, 0, MT_NUMDR, REG_HRO) },
142
{ URDATA (UST, mt_unit[0].UST, 8, 2, 0, MT_NUMDR, REG_HRO) },
143
{ ORDATA (CHAN, mt_dib.chan, 5), REG_HRO },
144
{ FLDATA (STOP_IOE, mt_stopioe, 0) },
148
{ MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },
149
{ MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },
150
{ MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
151
&sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
152
{ MTAB_XTD|MTAB_VDV, 0, NULL, "IOBUS",
153
&io_set_iobus, NULL, NULL },
154
{ MTAB_XTD|MTAB_VDV, 0, NULL, "DMC",
155
&io_set_dmc, NULL, NULL },
156
{ MTAB_XTD|MTAB_VDV, 0, NULL, "DMA",
157
&io_set_dma, NULL, NULL },
158
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL,
159
NULL, &io_show_chan, NULL },
163
"MT", mt_unit, mt_reg, mt_mod,
164
MT_NUMDR, 10, 31, 1, 8, 8,
165
NULL, NULL, &mt_reset,
166
NULL, &mt_attach, &mt_detach,
167
&mt_dib, DEV_DISABLE };
171
int32 mtio (int32 inst, int32 fnc, int32 dat, int32 dev)
174
UNIT *uptr = mt_dev.units + u;
175
static uint8 wrt_fnc[16] = { /* >0 = wr, 1 = chan op */
176
0, 0, 0, 0, 1, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0 };
178
switch (inst) { /* case on opcode */
180
mt_updint (mt_rdy, 0); /* clear motion intr */
181
mt_eof = 0; /* clear eof */
182
switch (fnc) { /* case on function */
183
case FNC_DMANM: /* set DMA/DMC */
185
mt_usel = u; /* save unit select */
186
if (mt_dib.chan) mt_dma = 1; /* if configured */
188
case FNC_IOBUS: /* set IOBUS */
189
mt_usel = u; /* save unit select */
192
case FNC_STOPW: /* stop write */
193
mt_usel = u; /* save unit select */
194
mt_updint (0, mt_mdirq); /* clear ready */
195
if (wrt_fnc[uptr->FNC & 017] == 1) /* writing? */
196
mt_eor = 1; /* set transfer done */
198
default: /* motion command */
199
if (mt_busy) return dat; /* nop if ctlr busy */
200
mt_eor = 0; /* clr transfer done */
201
mt_err = 0; /* clr error */
202
mt_usel = u; /* save unit select */
203
if (((uptr->flags & UNIT_ATT) == 0) || /* nop if not att */
204
sim_is_active (uptr)) /* or busy */
205
(IORETURN (mt_stopioe, SCPE_UNATT) | dat);
206
if (wrt_fnc[fnc] && (uptr->flags & UNIT_WPRT))
207
return (STOP_MTWRP << IOT_V_REASON);
211
sim_activate (uptr, mt_ctime); /* schedule */
214
case ioINA: /* INA */
215
if (fnc) return IOBADFNC (dat); /* fnc 0 only */
216
if (mt_rdy) { /* ready? */
217
mt_rdy = 0; /* clear ready */
218
return IOSKIP (dat | mt_buf); } /* ret buf, skip */
220
case ioOTA: /* OTA */
221
if (fnc) return IOBADFNC (dat); /* fnc 0 only */
222
if (mt_rdy) { /* ready? */
223
mt_rdy = 0; /* clear ready */
224
mt_buf = dat; /* store buf */
225
return IOSKIP (dat); } /* skip */
228
uptr = mt_dev.units + mt_usel; /* use saved unit sel */
230
case 000: /* ready */
231
if (mt_rdy) return IOSKIP (dat);
233
case 001: /* !busy */
234
if (!mt_busy) return IOSKIP (dat);
236
case 002: /* !error */
237
if (!mt_err) return IOSKIP (dat);
240
if (!(uptr->UST & STA_BOT)) return IOSKIP (dat);
242
case 004: /* !interrupting */
243
if (!TST_INTREQ (INT_MT)) return IOSKIP (dat);
246
if (!(uptr->UST & STA_EOT)) return IOSKIP (dat);
249
if (!mt_eof) return IOSKIP (dat);
251
case 007: /* !write prot */
252
if (!(uptr->flags & UNIT_WPRT)) return IOSKIP (dat);
254
case 011: /* operational */
255
if ((uptr->flags & UNIT_ATT) &&
256
((uptr->FNC & 017) != FNC_REW)) return IOSKIP (dat);
258
case 012: /* skip if !chan 2 */
260
case 013: /* skip if !auto */
262
case 014: /* !rewinding */
263
uptr = mt_dev.units + (dev & 03); /* use specified unit */
264
if ((uptr->FNC & 017) != FNC_REW) return IOSKIP (dat);
267
case ioEND: /* end of range */
268
mt_eor = 1; /* transfer done */
275
If rewind done, reposition to start of tape, set status
276
else, do operation, set done, interrupt
278
Can't be write locked, can only write lock detached unit
281
t_stat mt_svc (UNIT *uptr)
283
int32 ch = mt_dib.chan - 1; /* DMA/DMC ch */
284
uint32 i, c1, c2, c3;
286
t_stat st, r = SCPE_OK;
288
if ((uptr->flags & UNIT_ATT) == 0) { /* offline? */
291
mt_updint (0, 1); /* cmd done */
292
return IORETURN (mt_stopioe, SCPE_UNATT); }
294
switch (uptr->FNC) { /* case on function */
295
case FNC_REW: /* rewind (initial) */
296
mt_busy = 0; /* ctlr not busy */
297
uptr->FNC = uptr->FNC | FNC_2ND;
298
sim_activate (uptr, mt_ctime);
299
return SCPE_OK; /* continue */
301
case FNC_REW | FNC_2ND: /* rewind done */
302
uptr->pos = 0; /* reposition file */
303
uptr->UST = STA_BOT; /* set BOT */
304
uptr->FNC = FNC_NOP; /* nop function */
305
for (i = 0; i < MT_NUMDR; i++) { /* last rewind? */
306
if ((mt_unit[i].FNC & 017) == FNC_REW) return SCPE_OK; }
307
mt_updint (mt_rdy, 1); /* yes, motion done */
310
case FNC_WEOF: /* write file mark */
311
if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */
312
r = mt_map_err (uptr, st); /* map error */
313
break; /* sched end motion */
315
case FNC_FSR: /* space fwd rec */
316
if (st = sim_tape_sprecf (uptr, &tbc)) /* space fwd, err? */
317
r = mt_map_err (uptr, st); /* map error */
318
break; /* sched end motion */
320
case FNC_BSR: /* space rev rec */
321
if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */
322
r = mt_map_err (uptr, st); /* map error */
323
break; /* sched end motion */
325
case FNC_FSF: /* space fwd file */
326
while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;
327
r = mt_map_err (uptr, st); /* map error */
328
break; /* sched end motion */
330
case FNC_BSF: /* space rev file */
331
while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;
332
r = mt_map_err (uptr, st); /* map error */
333
break; /* sched end motion */
335
case FNC_EOM: /* end of motion */
336
uptr->FNC = FNC_NOP; /* nop function */
337
mt_busy = 0; /* not busy */
338
mt_updint (mt_rdy, 1); /* end of motion */
339
return SCPE_OK; /* done! */
341
/* Unit service, continued */
343
case FNC_RBCD2: case FNC_RBIN2: case FNC_RBIN3: /* read first */
344
mt_ptr = 0; /* clr buf ptr */
345
st = sim_tape_rdrecf (uptr, mtxb, &mt_max, DBSIZE); /* read rec */
346
if (st != MTSE_OK) { /* error? */
347
r = mt_map_err (uptr, st); /* map error */
348
break; } /* sched end motion */
349
uptr->FNC = uptr->FNC | FNC_2ND; /* next state */
350
sim_activate (uptr, mt_xtime); /* sched xfer */
353
case FNC_RBCD2 | FNC_2ND: /* read, word */
354
case FNC_RBIN2 | FNC_2ND:
355
case FNC_RBIN3 | FNC_2ND:
356
if (mt_ptr >= mt_max) break; /* record done? */
357
c1 = mtxb[mt_ptr++] & 077; /* get 2 chars */
358
c2 = mtxb[mt_ptr++] & 077;
359
if (uptr->FNC == (FNC_RBCD2 | FNC_2ND)) { /* BCD? */
360
if (c1 == 012) c1 = 0; /* change 12 to 0 */
361
if (c2 == 012) c2 = 0; }
362
if (uptr->FNC == (FNC_RBIN3 | FNC_2ND)) { /* read 3? */
363
if (mt_ptr >= mt_max) break; /* lose wd if not enuf */
364
c3 = mtxb[mt_ptr++] & 017; } /* get 3rd char */
366
sim_activate (uptr, mt_xtime); /* no, sched word */
367
if (mt_eor) return SCPE_OK; /* xfer done? */
368
mt_buf = (c1 << 10) | (c2 << 4) | c3; /* pack chars */
369
if (mt_rdy) mt_err = 1; /* buf full? err */
370
mt_updint (1, mt_mdirq); /* set ready */
371
if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */
372
return SCPE_OK; /* continue */
374
case FNC_WBCD2: case FNC_WBIN2: case FNC_WBIN3: /* write first */
375
mt_ptr = 0; /* clear buf ptr */
376
mt_updint (1, mt_mdirq); /* set ready */
377
if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */
378
uptr->FNC = uptr->FNC | FNC_2ND; /* next state */
379
sim_activate (uptr, mt_xtime); /* sched xfer */
380
return SCPE_OK; /* continue */
382
case FNC_WBCD2 | FNC_2ND: /* write, word */
383
case FNC_WBIN2 | FNC_2ND:
384
case FNC_WBIN3 | FNC_2ND:
385
if (mt_eor || mt_rdy) { /* done or no data? */
386
if (!mt_rdy) mt_wrwd (uptr, mt_buf); /* write last word */
387
if (mt_ptr) { /* any data? */
388
if (st = sim_tape_wrrecf (uptr, mtxb, mt_ptr)) /* write, err? */
389
r = mt_map_err (uptr, st); } /* map error */
390
break; } /* sched end motion */
391
mt_wrwd (uptr, mt_buf); /* write word */
392
sim_activate (uptr, mt_xtime); /* no, sched word */
393
mt_updint (1, mt_mdirq); /* set ready */
394
if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */
395
return SCPE_OK; /* continue */
397
default: /* unknown */
400
/* End of command, process error or schedule end of motion */
403
uptr->FNC = FNC_NOP; /* nop function */
404
mt_busy = 0; /* not busy */
405
mt_updint (mt_rdy, 1); /* end of motion */
407
uptr->FNC = FNC_EOM; /* sched end motion */
408
sim_activate (uptr, mt_ctime);
412
/* Write word to buffer */
414
void mt_wrwd (UNIT *uptr, uint32 dat)
418
c1 = (dat >> 10) & 077; /* get 2 chars */
419
c2 = (dat >> 4) & 077;
420
if (uptr->FNC == (FNC_WBCD2 | FNC_2ND)) { /* BCD? */
421
if (c1 == 0) c1 = 012; /* change 0 to 12 */
422
if (c2 == 0) c2 = 012; }
423
if (mt_ptr < DBSIZE) mtxb[mt_ptr++] = c1; /* store 2 char */
424
if (mt_ptr < DBSIZE) mtxb[mt_ptr++] = c2;
425
if ((uptr->FNC == (FNC_WBIN3 | FNC_2ND)) && /* write 3? */
426
(mt_ptr < DBSIZE)) mtxb[mt_ptr++] = mt_buf & 017;
430
/* Map tape error status */
432
t_stat mt_map_err (UNIT *uptr, t_stat st)
435
case MTSE_FMT: /* illegal fmt */
436
case MTSE_UNATT: /* unattached */
437
mt_err = 1; /* reject */
438
case MTSE_OK: /* no error */
439
return SCPE_IERR; /* never get here! */
440
case MTSE_TMK: /* end of file */
441
mt_eof = 1; /* eof */
443
case MTSE_INVRL: /* invalid rec lnt */
446
case MTSE_IOERR: /* IO error */
447
mt_err = 1; /* error */
448
if (mt_stopioe) return SCPE_IOERR;
450
case MTSE_RECE: /* record in error */
451
case MTSE_EOM: /* end of medium */
452
mt_err = 1; /* error */
454
case MTSE_BOT: /* reverse into BOT */
455
uptr->UST = STA_BOT; /* set status */
457
case MTSE_WRP: /* write protect */
458
mt_err = 1; /* error */
463
/* Update interrupts */
465
void mt_updint (uint32 rdy, uint32 mdirq)
467
mt_rdy = rdy; /* store new ready */
468
mt_mdirq = mdirq; /* store new motion irq */
469
if ((mt_rdy && !mt_dma) || mt_mdirq) SET_INT (INT_MT); /* update int request */
470
else CLR_INT (INT_MT);
476
t_stat mt_reset (DEVICE *dptr)
481
mt_buf = 0; /* clear state */
490
CLR_INT (INT_MT); /* clear int, enb */
492
for (i = 0; i < MT_NUMDR; i++) { /* loop thru units */
493
uptr = mt_dev.units + i;
494
sim_tape_reset (uptr); /* reset tape */
495
sim_cancel (uptr); /* cancel op */
496
uptr->UST = uptr->pos? 0: STA_BOT; /* update status */
497
uptr->FNC = FNC_NOP; }
503
t_stat mt_attach (UNIT *uptr, char *cptr)
507
r = sim_tape_attach (uptr, cptr); /* attach unit */
508
if (r != SCPE_OK) return r; /* update status */
515
t_stat mt_detach (UNIT* uptr)
517
uptr->UST = 0; /* update status */
518
uptr->FNC = FNC_NOP; /* nop function */
519
return sim_tape_detach (uptr); /* detach unit */