635 lines
22 KiB
C
635 lines
22 KiB
C
/* b5500_mt.c: Burrioughs 5500 Magnetic tape controller
|
||
|
||
Copyright (c) 2016, Richard Cornwell
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a
|
||
copy of this software and associated documentation files (the "Software"),
|
||
to deal in the Software without restriction, including without limitation
|
||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
and/or sell copies of the Software, and to permit persons to whom the
|
||
Software is furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in
|
||
all copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
||
Magnetic tapes are represented as a series of variable records
|
||
of the form:
|
||
|
||
32b byte count
|
||
byte 0
|
||
byte 1
|
||
:
|
||
byte n-2
|
||
byte n-1
|
||
32b byte count
|
||
|
||
If the byte count is odd, the record is padded with an extra byte
|
||
of junk. File marks are represented by a byte count of 0.
|
||
*/
|
||
|
||
#include "b5500_defs.h"
|
||
#include "sim_tape.h"
|
||
|
||
#if (NUM_DEVS_MT > 0)
|
||
|
||
#define BUFFSIZE 10240
|
||
#define UNIT_MT UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE
|
||
#define HT 500 /* Time per char high density */
|
||
|
||
|
||
/* in u4 is current buffer position */
|
||
/* in u5 Bits 30-16 of W */
|
||
#define CMD u5
|
||
#define POS u6
|
||
|
||
#define URCSTA_SKIP 000017 /* Skip mask */
|
||
#define URCSTA_SINGLE 000020 /* Single space skip. */
|
||
#define URCSTA_DOUBLE 000040 /* Double space skip */
|
||
#define URCSTA_READ 000400 /* Read flag */
|
||
#define URCSTA_WC 001000 /* Use word count */
|
||
#define URCSTA_DIRECT 002000 /* Direction, Long line */
|
||
#define URCSTA_BINARY 004000 /* Binary transfer */
|
||
#define URCSTA_INHIBIT 040000 /* Inhibit transfer to memory */
|
||
|
||
#define MT_CHAN 0000003 /* Channel active on */
|
||
#define MT_BIN 0000004 /* Binary/BCD */
|
||
#define MT_BACK 0000010 /* Backwards */
|
||
#define MT_CMD 0000070 /* Command to tape drive */
|
||
#define MT_INT 0000010 /* Interrogate */
|
||
#define MT_RD 0000020 /* Reading */
|
||
#define MT_RDBK 0000030 /* Reading Backwards */
|
||
#define MT_WR 0000040 /* Writing */
|
||
#define MT_REW 0000050 /* Rewind */
|
||
#define MT_FSR 0000060 /* Space Forward */
|
||
#define MT_BSR 0000070 /* Space Backward record */
|
||
#define MT_RDY 0000100 /* Device is ready for command */
|
||
#define MT_IDLE 0000200 /* Tape still in motion */
|
||
#define MT_MARK 0001000 /* Hit tape mark */
|
||
#define MT_EOT 0002000 /* At End Of Tape */
|
||
#define MT_BOT 0004000 /* At Beginning Of Tape */
|
||
#define MT_EOR 0010000 /* Set EOR on next record */
|
||
#define MT_BSY 0020000 /* Tape busy after operation */
|
||
#define MT_LOADED 0040000 /* Tape loaded, return ready */
|
||
|
||
|
||
#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF)
|
||
#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF
|
||
|
||
t_stat mt_srv(UNIT *);
|
||
t_stat mt_attach(UNIT *, CONST char *);
|
||
t_stat mt_detach(UNIT *);
|
||
t_stat mt_reset(DEVICE *);
|
||
t_stat mt_help(FILE *, DEVICE *, UNIT *, int32, const char *);
|
||
const char *mt_description(DEVICE *dptr);
|
||
|
||
/* One buffer per channel */
|
||
uint8 mt_buffer[NUM_CHAN][BUFFSIZE];
|
||
|
||
UNIT mt_unit[] = {
|
||
/* Controller 1 */
|
||
#if (NUM_DEVS_MT > 0)
|
||
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 3 */
|
||
#if (NUM_DEVS_MT > 3)
|
||
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 7 */
|
||
#if (NUM_DEVS_MT > 7)
|
||
{UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 8 */
|
||
{UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 9 */
|
||
{UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 10 */
|
||
{UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 11 */
|
||
#if (NUM_DEVS_MT > 11)
|
||
{UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 12 */
|
||
{UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 13 */
|
||
{UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 14 */
|
||
{UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 15 */
|
||
#endif
|
||
#endif
|
||
#endif
|
||
#endif
|
||
};
|
||
|
||
REG mt_reg[] = {
|
||
{CRDATA(BUFF, mt_buffer, 16, 8, sizeof(mt_buffer)), REG_HRO},
|
||
{0}
|
||
};
|
||
|
||
MTAB mt_mod[] = {
|
||
{ MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED",
|
||
&set_writelock, &show_writelock, NULL, "Write ring in place" },
|
||
{ MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED",
|
||
&set_writelock, NULL, NULL, "no Write ring in place" },
|
||
{MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT",
|
||
&sim_tape_set_fmt, &sim_tape_show_fmt, NULL,
|
||
"Set/Display tape format (SIMH, E11, TPC, P7B)" },
|
||
{MTAB_XTD | MTAB_VUN, 0, "LENGTH", "LENGTH",
|
||
&sim_tape_set_capac, &sim_tape_show_capac, NULL,
|
||
"Set unit n capacity to arg MB (0 = unlimited)" },
|
||
{MTAB_XTD | MTAB_VUN, 0, "DENSITY", "DENSITY",
|
||
NULL, &sim_tape_show_dens, NULL},
|
||
{0}
|
||
};
|
||
|
||
|
||
DEVICE mt_dev = {
|
||
"MT", mt_unit, mt_reg, mt_mod,
|
||
NUM_DEVS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, &mt_reset, NULL, &mt_attach, &mt_detach,
|
||
NULL, DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
|
||
NULL, NULL, &mt_help, NULL, NULL,
|
||
&mt_description
|
||
};
|
||
|
||
|
||
|
||
|
||
/* Start off a mag tape command */
|
||
t_stat
|
||
mt_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc)
|
||
{
|
||
UNIT *uptr;
|
||
int unit = dev >> 1;
|
||
|
||
/* Make sure valid drive number */
|
||
if (unit > (NUM_DEVS_MT - 1) || unit < 0)
|
||
return SCPE_NODEV;
|
||
|
||
uptr = &mt_unit[unit];
|
||
/* If unit disabled return error */
|
||
if (uptr->flags & UNIT_DIS)
|
||
return SCPE_NODEV;
|
||
|
||
if ((uptr->flags & UNIT_ATT) == 0)
|
||
return SCPE_UNATT;
|
||
|
||
/* Not there until loading done */
|
||
if ((uptr->CMD & MT_LOADED))
|
||
return SCPE_UNATT;
|
||
|
||
/* Check if drive is ready to recieve a command */
|
||
if ((uptr->CMD & MT_BSY) != 0)
|
||
return SCPE_BUSY;
|
||
|
||
/* Determine actual command */
|
||
uptr->CMD &= ~(MT_RDY|MT_CHAN|MT_CMD|MT_BIN);
|
||
uptr->CMD |= chan;
|
||
if (cmd & URCSTA_BINARY)
|
||
uptr->CMD |= MT_BIN;
|
||
if (cmd & URCSTA_READ) {
|
||
if ((cmd & URCSTA_WC) && *wc == 0)
|
||
uptr->CMD |= MT_FSR;
|
||
else
|
||
uptr->CMD |= MT_RD;
|
||
} else {
|
||
/* Erase gap not supported on sim, treat as
|
||
write of null record */
|
||
if ((cmd & URCSTA_WC) && *wc == 0)
|
||
uptr->CMD |= MT_INT;
|
||
else
|
||
uptr->CMD |= MT_WR;
|
||
}
|
||
|
||
*wc = 0; /* So no overide occurs */
|
||
|
||
/* Convert command to correct type */
|
||
if (cmd & URCSTA_DIRECT)
|
||
uptr->CMD |= MT_BACK;
|
||
uptr->POS = 0;
|
||
CLR_BUF(uptr);
|
||
sim_debug(DEBUG_CMD, &mt_dev, "Command %d %o %o\n", unit, uptr->CMD, cmd);
|
||
if ((uptr->CMD & MT_IDLE) == 0) {
|
||
sim_activate(uptr,50000);
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
|
||
/* Map simH errors into machine errors */
|
||
t_stat mt_error(UNIT * uptr, int chan, t_stat r, DEVICE * dptr)
|
||
{
|
||
switch (r) {
|
||
case MTSE_OK: /* no error */
|
||
sim_debug(DEBUG_EXP, dptr, "OK ");
|
||
break;
|
||
|
||
case MTSE_EOM: /* end of medium */
|
||
sim_debug(DEBUG_EXP, dptr, "EOT ");
|
||
if (uptr->CMD & MT_BOT) {
|
||
chan_set_blank(chan);
|
||
} else {
|
||
uptr->CMD &= ~MT_BOT;
|
||
uptr->CMD |= MT_EOT;
|
||
chan_set_eot(chan);
|
||
}
|
||
break;
|
||
|
||
case MTSE_TMK: /* tape mark */
|
||
sim_debug(DEBUG_EXP, dptr, "MARK ");
|
||
uptr->CMD &= ~(MT_BOT|MT_EOT);
|
||
chan_set_eof(chan);
|
||
break;
|
||
|
||
case MTSE_WRP: /* write protected */
|
||
sim_debug(DEBUG_EXP, dptr, "WriteLocked ");
|
||
chan_set_wrp(chan);
|
||
break;
|
||
|
||
case MTSE_INVRL: /* invalid rec lnt */
|
||
case MTSE_IOERR: /* IO error */
|
||
case MTSE_FMT: /* invalid format */
|
||
case MTSE_RECE: /* error in record */
|
||
chan_set_error(chan); /* Force redundency error */
|
||
sim_debug(DEBUG_EXP, dptr, "ERROR %d ", r);
|
||
break;
|
||
case MTSE_BOT: /* beginning of tape */
|
||
uptr->CMD &= ~MT_EOT;
|
||
uptr->CMD |= MT_BOT;
|
||
chan_set_bot(chan); /* Set flag */
|
||
sim_debug(DEBUG_EXP, dptr, "BOT ");
|
||
break;
|
||
case MTSE_UNATT: /* unattached */
|
||
default:
|
||
sim_debug(DEBUG_EXP, dptr, "%d ", r);
|
||
}
|
||
uptr->CMD &= ~(MT_CMD|MT_BIN);
|
||
uptr->CMD |= MT_RDY|MT_IDLE;
|
||
chan_set_end(chan);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Handle processing of tape requests. */
|
||
t_stat mt_srv(UNIT * uptr)
|
||
{
|
||
int chan = uptr->CMD & MT_CHAN;
|
||
int unit = uptr - mt_unit;
|
||
int cmd = uptr->CMD & MT_CMD;
|
||
DEVICE *dptr = find_dev_from_unit(uptr);
|
||
t_mtrlnt reclen;
|
||
t_stat r = SCPE_ARG; /* Force error if not set */
|
||
uint8 ch;
|
||
int mode;
|
||
t_mtrlnt loc;
|
||
|
||
|
||
/* Simulate tape load delay */
|
||
if (uptr->CMD & MT_LOADED) {
|
||
uptr->CMD &= ~MT_LOADED;
|
||
uptr->CMD |= MT_BSY|MT_RDY;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unit=%d Loaded\n", unit);
|
||
sim_activate(uptr, 50000);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
if (uptr->CMD & MT_BSY) {
|
||
uptr->CMD &= ~MT_BSY;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unit=%d Online\n", unit);
|
||
iostatus |= 1 << (uptr - mt_unit);
|
||
if (uptr->CMD & MT_IDLE)
|
||
sim_activate(uptr, 50000);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
if (uptr->CMD & MT_IDLE) {
|
||
uptr->CMD &= ~MT_IDLE;
|
||
if (uptr->CMD & MT_RDY) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unit=%d idling\n", unit);
|
||
return SCPE_OK;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unit=%d start %02o\n", unit, cmd);
|
||
}
|
||
|
||
switch (cmd) {
|
||
/* Handle interrogate */
|
||
case MT_INT:
|
||
if (sim_tape_wrp(uptr))
|
||
chan_set_wrp(chan);
|
||
uptr->CMD &= ~(MT_CMD|MT_BIN);
|
||
uptr->CMD |= MT_RDY;
|
||
chan_set_end(chan);
|
||
sim_debug(DEBUG_DETAIL, dptr, "Status\n");
|
||
return SCPE_OK;
|
||
|
||
case MT_RD: /* Read */
|
||
/* If at end of record, fill buffer */
|
||
if (BUF_EMPTY(uptr)) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d %s ", unit,
|
||
(uptr->CMD & MT_BIN)? "bin": "bcd");
|
||
if (sim_tape_eot(uptr)) {
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, MTSE_EOM, dptr);
|
||
}
|
||
r = sim_tape_rdrecf(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE);
|
||
if (r != MTSE_OK) {
|
||
if (r == MTSE_TMK) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "TM\n");
|
||
ch = 017;
|
||
(void)chan_write_char(chan, &ch, 1);
|
||
sim_activate(uptr, 4000);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "r=%d\n", r);
|
||
sim_activate(uptr, 5000);
|
||
}
|
||
return mt_error(uptr, chan, r, dptr);
|
||
} else {
|
||
uptr->CMD &= ~(MT_BOT|MT_EOT);
|
||
uptr->hwmark = reclen;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d chars\n", uptr->hwmark);
|
||
uptr->POS = 0;
|
||
if ((uptr->CMD & MT_BIN) == 0)
|
||
mode = 0100;
|
||
else
|
||
mode = 0;
|
||
for (loc = 0; loc < reclen; loc++) {
|
||
ch = mt_buffer[chan][loc] & 0177;
|
||
if (((parity_table[ch & 077]) ^ (ch & 0100) ^ mode) == 0) {
|
||
chan_set_error(chan);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
ch = mt_buffer[chan][uptr->POS++] & 0177;
|
||
/* 00 characters are not transfered in BCD mode */
|
||
if (ch == 0) {
|
||
if (((uint32)uptr->POS) >= uptr->hwmark) {
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, MTSE_OK, dptr);
|
||
} else {
|
||
sim_activate(uptr, HT);
|
||
return SCPE_OK;
|
||
}
|
||
}
|
||
|
||
if (chan_write_char(chan, &ch,
|
||
(((uint32)uptr->POS) >= uptr->hwmark) ? 1 : 0)) {
|
||
sim_debug(DEBUG_DATA, dptr, "Read unit=%d %d EOR\n", unit,
|
||
uptr->hwmark-uptr->POS);
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, MTSE_OK, dptr);
|
||
} else {
|
||
sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %03o\n",
|
||
unit, uptr->POS, ch);
|
||
sim_activate(uptr, HT);
|
||
}
|
||
return SCPE_OK;
|
||
|
||
case MT_RDBK: /* Read Backword */
|
||
/* If at end of record, fill buffer */
|
||
if (BUF_EMPTY(uptr)) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read back unit=%d %s ", unit,
|
||
(uptr->CMD & MT_BIN)? "bin": "bcd");
|
||
if (sim_tape_bot(uptr)) {
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, MTSE_BOT, dptr);
|
||
}
|
||
r = sim_tape_rdrecr(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE);
|
||
if (r != MTSE_OK) {
|
||
if (r == MTSE_TMK) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "TM\n");
|
||
ch = 017;
|
||
(void)chan_write_char(chan, &ch, 1);
|
||
sim_activate(uptr, 4000);
|
||
} else {
|
||
uptr->CMD |= MT_BSY;
|
||
sim_debug(DEBUG_DETAIL, dptr, "r=%d\n", r);
|
||
sim_activate(uptr, 100);
|
||
}
|
||
return mt_error(uptr, chan, r, dptr);
|
||
} else {
|
||
uptr->CMD &= ~(MT_BOT|MT_EOT);
|
||
uptr->hwmark = reclen;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d chars\n", uptr->hwmark);
|
||
uptr->POS = uptr->hwmark;
|
||
if ((uptr->CMD & MT_BIN) == 0)
|
||
mode = 0100;
|
||
else
|
||
mode = 0;
|
||
for (loc = 0; loc < reclen; loc++) {
|
||
ch = mt_buffer[chan][loc] & 0177;
|
||
if (((parity_table[ch & 077]) ^ (ch & 0100) ^ mode) == 0) {
|
||
chan_set_error(chan);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
ch = mt_buffer[chan][--uptr->POS] & 0177;
|
||
/* 00 characters are not transfered in BCD mode */
|
||
if (ch == 0) {
|
||
if (uptr->POS <= 0) {
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, MTSE_OK, dptr);
|
||
} else {
|
||
sim_activate(uptr, HT);
|
||
return SCPE_OK;
|
||
}
|
||
}
|
||
|
||
if (chan_write_char(chan, &ch, (uptr->POS > 0) ? 0 : 1)) {
|
||
sim_debug(DEBUG_DATA, dptr, "Read back unit=%d %d EOR\n",
|
||
unit, uptr->hwmark-uptr->POS);
|
||
sim_activate(uptr, 100);
|
||
return mt_error(uptr, chan, MTSE_OK, dptr);
|
||
} else {
|
||
sim_debug(DEBUG_DATA, dptr, "Read back data unit=%d %d %03o\n",
|
||
unit, uptr->POS, ch);
|
||
sim_activate(uptr, HT);
|
||
}
|
||
return SCPE_OK;
|
||
|
||
case MT_WR: /* Write */
|
||
/* Check if write protected */
|
||
if (uptr->POS == 0 && sim_tape_wrp(uptr)) {
|
||
sim_activate(uptr, 100);
|
||
return mt_error(uptr, chan, MTSE_WRP, dptr);
|
||
}
|
||
if (chan_read_char(chan, &ch,
|
||
(uptr->POS > BUFFSIZE) ? 1 : 0)) {
|
||
reclen = uptr->POS;
|
||
/* If no transfer, then either erase */
|
||
if (reclen == 0) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Erase\n");
|
||
r = MTSE_OK;
|
||
} else if ((reclen == 1) && (cmd & MT_BIN) == 0 &&
|
||
(mt_buffer[chan][0] == 017)) {
|
||
/* Check if write rtape mark */
|
||
sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit);
|
||
r = sim_tape_wrtmk(uptr);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr,
|
||
"Write unit=%d Block %d %s chars\n", unit, reclen,
|
||
(uptr->CMD & MT_BIN)? "bin": "bcd");
|
||
r = sim_tape_wrrecf(uptr, &mt_buffer[chan][0], reclen);
|
||
}
|
||
uptr->CMD &= ~(MT_BOT|MT_EOT);
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, r, dptr); /* Record errors */
|
||
} else {
|
||
/* Copy data to buffer */
|
||
ch &= 077;
|
||
ch |= parity_table[ch];
|
||
if ((uptr->CMD & MT_BIN))
|
||
ch ^= 0100;
|
||
/* Don't write out even parity zeros */
|
||
if (ch != 0)
|
||
mt_buffer[chan][uptr->POS++] = ch;
|
||
sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %03o\n",
|
||
unit, uptr->POS, ch);
|
||
uptr->hwmark = uptr->POS;
|
||
}
|
||
sim_activate(uptr, HT);
|
||
return SCPE_OK;
|
||
|
||
case MT_FSR: /* Space forward one record */
|
||
if (BUF_EMPTY(uptr)) {
|
||
/* If at end of record, fill buffer */
|
||
sim_debug(DEBUG_DETAIL, dptr, "Space unit=%d ", unit);
|
||
if (sim_tape_eot(uptr)) {
|
||
uptr->CMD &= ~MT_BOT;
|
||
sim_debug(DEBUG_DETAIL, dptr, "EOT\n");
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, MTSE_EOM, dptr);
|
||
}
|
||
r = sim_tape_rdrecf(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE);
|
||
if (r != MTSE_OK) {
|
||
if (r == MTSE_TMK) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "TM ");
|
||
reclen = 1;
|
||
chan_set_eof(chan);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "r=%d ", r);
|
||
reclen = 10;
|
||
}
|
||
}
|
||
uptr->CMD &= ~(MT_BOT|MT_EOT);
|
||
uptr->hwmark = reclen;
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d chars\n", uptr->hwmark);
|
||
sim_activate(uptr, uptr->hwmark * HT);
|
||
return SCPE_OK;
|
||
}
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, MTSE_OK, dptr);
|
||
|
||
case MT_BSR: /* Backspace record */
|
||
if (BUF_EMPTY(uptr)) {
|
||
/* If at end of record, fill buffer */
|
||
sim_debug(DEBUG_DETAIL, dptr, "backspace unit=%d ", unit);
|
||
if (sim_tape_bot(uptr)) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "BOT\n");
|
||
sim_activate(uptr, 100);
|
||
return mt_error(uptr, chan, MTSE_BOT, dptr);
|
||
}
|
||
r = sim_tape_rdrecr(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE);
|
||
if (r != MTSE_OK) {
|
||
if (r == MTSE_TMK) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "TM ");
|
||
reclen = 1;
|
||
chan_set_eof(chan);
|
||
} else {
|
||
reclen = 10;
|
||
sim_debug(DEBUG_DETAIL, dptr, "r=%d ", r);
|
||
}
|
||
}
|
||
uptr->CMD &= ~(MT_BOT|MT_EOT);
|
||
uptr->hwmark = reclen;
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d chars\n", uptr->hwmark);
|
||
sim_activate(uptr, uptr->hwmark * HT);
|
||
return SCPE_OK;
|
||
}
|
||
sim_activate(uptr, 4000);
|
||
return mt_error(uptr, chan, MTSE_OK, dptr);
|
||
|
||
case MT_REW: /* Rewind */
|
||
sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d pos=%d\n", unit,
|
||
uptr->pos);
|
||
uptr->CMD &= ~(MT_CMD | MT_BIN | MT_IDLE | MT_RDY);
|
||
uptr->CMD |= MT_BSY|MT_RDY;
|
||
iostatus &= ~(1 << (uptr - mt_unit));
|
||
sim_activate(uptr, (uptr->pos/100) + 100);
|
||
r = sim_tape_rewind(uptr);
|
||
uptr->CMD &= ~MT_EOT;
|
||
uptr->CMD |= MT_BOT;
|
||
chan_set_end(chan);
|
||
return r;
|
||
}
|
||
return mt_error(uptr, chan, r, dptr);
|
||
}
|
||
|
||
|
||
t_stat
|
||
mt_attach(UNIT * uptr, CONST char *file)
|
||
{
|
||
t_stat r;
|
||
|
||
if ((r = sim_tape_attach_ex(uptr, file, 0, 0)) != SCPE_OK)
|
||
return r;
|
||
if ((sim_switches & SIM_SW_REST) == 0)
|
||
uptr->CMD |= MT_LOADED|MT_BOT;
|
||
sim_activate(uptr, 50000);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
mt_detach(UNIT * uptr)
|
||
{
|
||
uptr->CMD = 0;
|
||
iostatus &= ~(1 << (uptr - mt_unit));
|
||
return sim_tape_detach(uptr);
|
||
}
|
||
|
||
t_stat
|
||
mt_reset(DEVICE *dptr)
|
||
{
|
||
int i;
|
||
|
||
/* Scan all devices and enable those that
|
||
are loaded. This is to allow tapes that
|
||
are mounted prior to boot to be recognized
|
||
at later. Also disconnect all devices no
|
||
longer connected. */
|
||
for ( i = 0; i < NUM_DEVS_MT; i++) {
|
||
if ((mt_unit[i].flags & UNIT_ATT) == 0)
|
||
iostatus &= ~(1 << i);
|
||
else if (mt_unit[i].CMD & (MT_LOADED|MT_RDY)) {
|
||
iostatus |= 1 << i;
|
||
mt_unit[i].CMD &= ~(MT_LOADED);
|
||
mt_unit[i].CMD |= MT_RDY;
|
||
}
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
||
{
|
||
fprintf (st, "B422/B423 Magnetic tape unit\n\n");
|
||
fprintf (st, "The magnetic tape controller assumes that all tapes are 7 track\n");
|
||
fprintf (st, "with valid parity. Tapes are assumed to be 555.5 characters per\n");
|
||
fprintf (st, "inch. To simulate a standard 2400foot tape, do:\n");
|
||
fprintf (st, " sim> SET MTn LENGTH 15\n\n");
|
||
fprintf (st, "By default only 8 drives are enabled, additional units up to 15 supported.\n");
|
||
fprint_set_help(st, dptr);
|
||
fprint_show_help(st, dptr);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *
|
||
mt_description(DEVICE *dptr)
|
||
{
|
||
return "B422/B423 Magnetic tape unit";
|
||
}
|
||
#endif
|
||
|
||
|