/* 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


