/* icl1900_mta.c: ICL1900 1974 mag tape drive simulator.

   Copyright (c) 2018, 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.

   Except as contained in this notice, the name of Richard Cornwell shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Richard Cornwell

   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 "icl1900_defs.h"
#include "sim_tape.h"

#if (NUM_DEVS_MTA > 0)
#define BUFFSIZE       (64 * 1024)
#define MTUF_9TR       (1 << MTUF_V_UF)
#define UNIT_MTA             UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | MTUF_9TR
#define DEV_BUF_NUM(x)  (((x) & 07) << DEV_V_UF)
#define GET_DEV_BUF(x)  (((x) >> DEV_V_UF) & 07)

#define CMD          u3             /* Command */
#define STATUS       u4
#define ADDR         u5             /* Transfer address read from M[64+n] */
#define POS          u6             /* Position within buffer */


/*  Command is packed follows:
 *
 *   Lower 3 bits is command.
 *   Next bit is binary/BCD.
 *   Next bit is disconnect flag.
 *   Top 16 bits are count.
 */

#define MT_CMD      007
#define BCD         010
#define DISC        020

#define BUF_EMPTY(u)  (u->hwmark == 0xFFFFFFFF)
#define CLR_BUF(u)     u->hwmark =  0xFFFFFFFF

#define MT_READ      0
#define MT_WRITE     1
#define MT_REV_READ  2
#define MT_WRITEERG  3
#define MT_SKIPF     4
#define MT_WTM       5
#define MT_SKIPB     6
#define MT_REW       7

#define MT_TRCNT     M15
#define MT_STOP      B3
#define MT_START     B4
#define MT_BCD       B5

/* Error status word */
#define TERMINATE    000000001        /* Transfer complete */
#define OPAT         000000002        /* Operator attention */
#define PARITY       000000004        /* Parity error */
#define HESFAIL      000000010        /* Failed to transfer word in time */
#define ACCEPT       000000020        /* Ready for command */
#define BUSY         000000040        /* Device busy */
#define CBUSY        000000100        /* Controller Busy */
#define WPROT        000001000        /* Write protect */
#define BOT          000002000        /* Beginning of Tape */
#define EOT          000004000        /* End of Tape. */
#define OFFLINE      000040000        /* Device offline */
#define LONGBLK      000100000        /* Long block */
#define FILLWRD      000200000        /* Block short filled with stop */
#define MARK         000400000        /* Tape mark sensed */
#define DENS         014000000
#define CHAR         060000000        /* Count of character read */

int  mta_busy;   /* Indicates that controller is talking to a drive */
uint8 mta_buffer[BUFFSIZE];
void mta_nsi_cmd (int dev, uint32 cmd);
void mta_nsi_status (int dev, uint32 *resp);
t_stat mta_svc (UNIT *uptr);
t_stat mta_reset (DEVICE *dptr);
t_stat mta_boot (int32 unit_num, DEVICE * dptr);
t_stat mta_attach(UNIT *, CONST char *);
t_stat mta_detach(UNIT *);
t_stat mta_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
CONST char *mta_description (DEVICE *dptr);

DIB mta_dib = {  WORD_DEV|BLK_DEV, NULL, &mta_nsi_cmd, &mta_nsi_status };


MTAB                mta_mod[] = {
    {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL},
    {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL},
    {MTUF_9TR, 0, "7 track", "7T", NULL},
    {MTUF_9TR, MTUF_9TR, "9 track", "9T", NULL},
    {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT",
         &sim_tape_set_fmt, &sim_tape_show_fmt, NULL},
    {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "DEV", "DEV",
         &set_chan, &get_chan, NULL, "Device Number"},
    {0}
};

UNIT                mta_unit[] = {
    {UDATA(&mta_svc, UNIT_MTA, 0) },       /* 0 */
    {UDATA(&mta_svc, UNIT_MTA, 0) },       /* 1 */
    {UDATA(&mta_svc, UNIT_MTA, 0) },       /* 2 */
    {UDATA(&mta_svc, UNIT_MTA, 0) },       /* 3 */
    {UDATA(&mta_svc, UNIT_MTA, 0) },       /* 4 */
    {UDATA(&mta_svc, UNIT_MTA, 0) },       /* 5 */
    {UDATA(&mta_svc, UNIT_MTA, 0) },       /* 6 */
    {UDATA(&mta_svc, UNIT_MTA, 0) },       /* 7 */
};

DEVICE mta_dev = {
    "MTA", mta_unit, NULL, mta_mod,
    NUM_DEVS_MTA, 8, 22, 1, 8, 22,
    NULL, NULL, &mta_reset, &mta_boot, &mta_attach, &mta_detach,
    &mta_dib, DEV_DIS | DEV_DISABLE | DEV_DEBUG | UNIT_ADDR(24), 0, dev_debug,
    NULL, NULL, &mta_help, NULL, NULL, &mta_description
    };

void mta_nsi_cmd(int dev, uint32 cmd) {
   int32    d;
   UNIT    *uptr;

   d = dev - GET_UADDR(mta_dev.flags);
   sim_debug(DEBUG_CMD, &mta_dev, "CMD: %d: %d c=%08o\n", dev, d, cmd);
   if (d < 0 || d > (int32)mta_dev.numunits)
       return;
   uptr = &mta_dev.units[d];

   if (cmd & MT_STOP) {
       uptr->CMD |= DISC;
       return;
   }

   if (cmd & MT_START) {
       if (mta_busy)
           return;
       if ((uptr->STATUS & BUSY) || (uptr->flags & UNIT_ATT) == 0) {
           uptr->STATUS |= OPAT;
           chan_set_done(GET_UADDR(uptr->flags));
           return;
       }
       uptr->CMD = (cmd & MT_TRCNT) << 16;
       uptr->CMD |= (cmd >> 15) & 07;
       if (cmd & MT_BCD)
          uptr->CMD |= BCD;
       uptr->STATUS = BUSY;
       uptr->ADDR = M[64 + dev] & M15;  /* Get transfer address */
       uptr->POS = 0;
       CLR_BUF(uptr);
       mta_busy = 1;
       sim_activate(uptr, 100);
       chan_clr_done(GET_UADDR(uptr->flags));
   }
}

void mta_nsi_status(int dev, uint32 *resp) {
   int32    d;
   UNIT    *uptr;

   *resp = 0;
   d = dev - GET_UADDR(mta_dev.flags);
   if (d < 0 || d > (int32)mta_dev.numunits)
       return;
   uptr = &mta_dev.units[d];

   *resp = uptr->STATUS;
   if (mta_busy)
       *resp |= CBUSY;
   /* Set hard status bits. */
   if ((uptr->flags & UNIT_ATT) == 0)
       *resp |= OFFLINE;
   if (sim_tape_wrp(uptr))
       *resp |= WPROT;
   if (sim_tape_bot(uptr))
       *resp |= BOT;
   if (sim_tape_eot(uptr))
       *resp |= EOT;
   sim_debug(DEBUG_CMD, &mta_dev, "STAT: %d: %d c=%08o\n", dev, d, *resp);
   chan_clr_done(dev);
}

t_stat mta_svc (UNIT *uptr)
{
    DEVICE      *dptr = &mta_dev;
    int          unit = (uptr - dptr->units);
    int          dev = unit + GET_UADDR(dptr->flags);
    t_mtrlnt     reclen;
    t_stat       r;
    uint8        ch;
    uint32       word;
    int          i;
    int          stop;
    uint8        mode;

    /* Handle a disconnect request */
    if (uptr->CMD & DISC) {
       uptr->STATUS &= ~BUSY;
       mta_busy = 0;
       chan_set_done(dev);
       return SCPE_OK;
    }
    /* If not busy, false schedule, just exit */
    if ((uptr->STATUS & BUSY) == 0)
        return SCPE_OK;

    switch (uptr->CMD & MT_CMD) {
    case MT_READ:
         /* If empty buffer, fill */
         if (BUF_EMPTY(uptr)) {
             sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit);
             if ((r = sim_tape_rdrecf(uptr, &mta_buffer[0], &reclen,
                              BUFFSIZE)) != MTSE_OK) {
                 sim_debug(DEBUG_DETAIL, dptr, " error %d\n", r);
                 if (r == MTSE_TMK)
                     uptr->STATUS |= MARK;
                 else if (r == MTSE_WRP)
                     uptr->STATUS |= WPROT;
                 else if (r == MTSE_EOM)
                     uptr->STATUS |= EOT;
                 else if (r == MTSE_UNATT)
                     uptr->STATUS |= OFFLINE|OPAT;
                 else
                     uptr->STATUS |= OPAT;
                 uptr->STATUS |= TERMINATE;
                 uptr->STATUS &= ~BUSY;
                 mta_busy = 0;
                 chan_set_done(dev);
                 return SCPE_OK;
             }
             uptr->hwmark = reclen;
             sim_debug(DEBUG_DETAIL, dptr, "Block %d chars\n", reclen);
         }
         stop = 0;
         if (uptr->flags & MTUF_9TR) {
             /* Grab three chars off buffer */
             word = 0;
             uptr->STATUS &= ~CMASK;
             for(i = 16; i >= 0; i-=8) {
                 if ((uint32)uptr->POS >= uptr->hwmark) {
                    /* Add in fill characters */
                    stop = 1;
                    if (i == 8) {
                       uptr->STATUS += B2|B1;
                       word |= 074;
                    } else if (i == 16) {
                       uptr->STATUS += B1;
                       word |= 07474;
                    }
                    break;
                 }
                 word |= (uint32)mta_buffer[uptr->POS++] << i;
             }
             uptr->STATUS |= BM1;
         } else {
             /* Grab four chars and check parity */
             word = 0;
             mode = (uptr->CMD & BCD) ? 0 : 0100;
             uptr->STATUS &= ~CMASK;
             for(i = 18; i >= 0; i-=6) {
                 if (stop || (uint32)uptr->POS >= uptr->hwmark) {
                    stop = 1;
                    ch = 074;
                 } else {
                    ch = mta_buffer[uptr->POS++];
                    if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) {
                        sim_debug(DEBUG_DETAIL, dptr, "Parity error unit=%d %d %03o\n",
                              unit, uptr->POS-1, ch);
                        uptr->STATUS |= PARITY;
                        break;
                    }
                    uptr->STATUS += B1;
                 }
                 word |= (ch & 077) << i;
             }
         }
         sim_debug(DEBUG_DATA, dptr, "unit=%d %08o read %08o\n", unit, uptr->ADDR, word);
         if (stop || (uptr->STATUS & (CMASK|BM1)) != 0) {
             if (uptr->ADDR < 8)
                 XR[uptr->ADDR] = word;
             M[uptr->ADDR++] = word;
             uptr->ADDR &= M15;
             uptr->CMD -= 1 << 16;
             if (stop || (uptr->CMD & (M15 << 16)) == 0 || (uint32)uptr->POS >= uptr->hwmark) {
                 /* Done with transfer */
                 sim_debug(DEBUG_DETAIL, dptr, "unit=%d %08o left %08o\n", unit, uptr->ADDR,
                                    uptr->CMD >> 16);
                 if ((uptr->CMD & (M15 << 16)) == 0 && (uint32)uptr->POS < uptr->hwmark)
                     uptr->STATUS |= LONGBLK;
                 if ((uptr->CMD & BCD) != 0 && (uptr->CMD & (M15 << 16)) != 0
                                  && (uint32)uptr->POS >= uptr->hwmark) {
                     uptr->STATUS |= FILLWRD;
                     M[uptr->ADDR++] = 074747474;
                     uptr->ADDR &= M15;
                 }
                 uptr->STATUS |= TERMINATE;
                 uptr->STATUS &= ~BUSY;
                 M[64 + dev] = uptr->ADDR;  /* Get transfer address */
                 mta_busy = 0;
                 uptr->STATUS &= FMASK;
                 chan_set_done(dev);
                 return SCPE_OK;
             }
             uptr->STATUS &= FMASK;
         }
         sim_activate(uptr, 100);
         break;

    case MT_WRITEERG: /* Write and Erase */
    case MT_WRITE:
         /* Check if write protected */
         if (sim_tape_wrp(uptr)) {
             uptr->STATUS |= WPROT;
             uptr->STATUS &= ~BUSY;
             mta_busy = 0;
             uptr->STATUS &= FMASK;
             chan_set_done(dev);
             return SCPE_OK;
         }

         word = M[uptr->ADDR++];
         uptr->ADDR &= M15;
         uptr->CMD -= 1 << 16;
         sim_debug(DEBUG_DATA, dptr, "unit=%d %08o write %08o\n", unit, uptr->ADDR, word);

         stop = 0;
         if (uptr->flags & MTUF_9TR) {
             /* Put three chars in buffer */
             uptr->STATUS &= ~CMASK;
             for(i = 16; i >= 0; i-=8) {
                 mta_buffer[uptr->POS++] = (uint8)((word >> i) & 0xff);
                 uptr->STATUS += B1;
             }
             /* Check if end character detected */
             if ((uptr->CMD & BCD) != 0) {
                 for (i = 0; i <= 18; i+= 6) {
                     if (((word >> i) & 077)  == 074) {
                         uptr->POS--;
                         uptr->STATUS -= B1;
                         stop = 1;
                     }
                 }
             }
         } else {
             /* Put four chars and generate parity */
             mode = (uptr->CMD & BCD) ? 0 : 0100;
             uptr->STATUS &= ~CMASK;
             for(i = 18; i >= 0; i-=6) {
                 ch = (uint8)((word >> i) & 077);
                 if ((uptr->CMD & BCD) != 0 && ch == 074) {
                     stop = 1;
                     break;
                 }
                 ch |= parity_table[ch] ^ mode;
                 mta_buffer[uptr->POS++] = ch;
                 uptr->STATUS += B1;
             }
         }
         uptr->STATUS &= FMASK;
         uptr->hwmark = uptr->POS;
         if (stop || (uptr->CMD & (M15 << 16)) == 0) {
             /* Done with transfer */
             reclen = uptr->hwmark;
             sim_debug(DEBUG_DETAIL, dptr, "Write unit=%d Block %d chars\n",
                      unit, reclen);
             r = sim_tape_wrrecf(uptr, &mta_buffer[0], reclen);
             if (r != MTSE_OK)
                uptr->STATUS |= OPAT;
             uptr->STATUS |= TERMINATE;
             uptr->STATUS &= ~BUSY;
             mta_busy = 0;
             uptr->STATUS &= FMASK;
             M[64 + dev] = uptr->ADDR;  /* Set transfer address */
             chan_set_done(dev);
             return SCPE_OK;
         }
         sim_activate(uptr, 100);
         break;

    case MT_REV_READ:
         /* If empty buffer, fill */
         if (BUF_EMPTY(uptr)) {
             if (sim_tape_bot(uptr)) {
                 uptr->STATUS |= OPAT|TERMINATE;
                 uptr->STATUS &= ~BUSY;
                 mta_busy = 0;
                 chan_set_done(dev);
                 return SCPE_OK;
             }
             sim_debug(DEBUG_DETAIL, dptr, "Read rev unit=%d ", unit);
             if ((r = sim_tape_rdrecr(uptr, &mta_buffer[0], &reclen,
                              BUFFSIZE)) != MTSE_OK) {
                 sim_debug(DEBUG_DETAIL, dptr, " error %d\n", r);
                 if (r == MTSE_TMK)
                     uptr->STATUS |= MARK;
                 else if (r == MTSE_WRP)
                     uptr->STATUS |= WPROT;
                 else if (r == MTSE_EOM)
                     uptr->STATUS |= EOT;
                 else if (r == MTSE_UNATT)
                     uptr->STATUS |= OFFLINE|OPAT;
                 else
                     uptr->STATUS |= OPAT;
                 uptr->STATUS |= TERMINATE;
                 uptr->STATUS &= ~BUSY;
                 mta_busy = 0;
                 chan_set_done(dev);
                 return SCPE_OK;
             }
             uptr->POS = reclen;
             uptr->ADDR += (uptr->CMD >> 16) + 1;
             uptr->hwmark = reclen;
             sim_debug(DEBUG_DETAIL, dptr, "Block %d chars\n", reclen);
         }

         stop = 0;
         if (uptr->flags & MTUF_9TR) {
             /* Grab three chars off buffer */
             word = 0;
             uptr->STATUS &= ~CMASK;
             for(i = 0; i <= 16; i+=8) {
                 word |= (uint32)mta_buffer[--uptr->POS] << i;
                 if (uptr->POS == 0) {
                    stop = 1;
                    break;
                 }
             }
             uptr->STATUS |= BM1;
         } else {
             /* Grab four chars and check parity */
             word = 0;
             mode = (uptr->CMD & BCD) ? 0 : 0100;
             uptr->STATUS &= ~CMASK;
             for(i = 0; i <= 16; i+=6) {
                 if (uptr->POS == 0) {
                     ch = 074;
                     stop = 1;
                 } else {
                     ch = mta_buffer[--uptr->POS];
                     if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) {
                         sim_debug(DEBUG_DETAIL, dptr, "Parity error unit=%d %d %03o\n",
                               unit, uptr->POS, ch);
                         uptr->STATUS |= PARITY;
                         break;
                     }
                     uptr->STATUS += B1;
                 }
                 word |= (ch & 077) << i;
             }
         }
         sim_debug(DEBUG_DATA, dptr, "unit=%d %08o read %08o\n", unit, uptr->ADDR, word);
         if (stop || (uptr->STATUS & (CMASK|BM1)) != 0) {
             uptr->ADDR = (uptr->ADDR - 1) & M15;
             if (uptr->ADDR < 8)
                 XR[uptr->ADDR] = word;
             M[uptr->ADDR] = word;
             uptr->CMD -= 1 << 16;
             if (stop || (uptr->CMD & (M15 << 16)) == 0 || uptr->POS == 0) {
                 sim_debug(DEBUG_DETAIL, dptr, "unit=%d %08o left %08o\n", unit, uptr->ADDR,
                                             uptr->CMD >> 16);
                 /* Done with transfer */
                 if ((uptr->CMD & (M15 << 16)) == 0 && uptr->POS != 0)
                     uptr->STATUS |= LONGBLK;
                 if ((uptr->CMD & BCD) != 0 && (uptr->CMD & (M15 << 16)) != 0
                                  && uptr->POS != 0) {
                     uptr->STATUS |= FILLWRD;
                     uptr->ADDR = (uptr->ADDR - 1) & M15;
                     M[uptr->ADDR] = 074747474;
                     uptr->ADDR &= M15;
                 }
                 uptr->STATUS |= TERMINATE;
                 uptr->STATUS &= ~BUSY;
                 M[64 + dev] = uptr->ADDR;  /* Set transfer address */
                 mta_busy = 0;
                 uptr->STATUS &= FMASK;
                 chan_set_done(dev);
                 return SCPE_OK;
             }
             uptr->STATUS &= FMASK;
         }
         sim_activate(uptr, 100);
         break;

    case MT_SKIPF:
         switch(uptr->POS) {
         case 0:
              uptr->POS ++;
              sim_activate(uptr, 500);
              break;
         case 1:
              sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d ", unit);
              r = sim_tape_sprecf(uptr, &reclen);
              if (r == MTSE_TMK) {
                  uptr->POS++;
                  sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
                  sim_activate(uptr, 50);
              } else if (r == MTSE_EOM) {
                  uptr->POS++;
                  uptr->STATUS |= EOT;
                  sim_activate(uptr, 50);
              } else {
                  sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen);
                  sim_activate(uptr, 10 + (10 * reclen));
              }
              break;
         case 2:
              uptr->STATUS &= ~BUSY;
              uptr->STATUS |= TERMINATE;
//              uptr->STATUS |= TERMINATE|MARK;
              mta_busy = 0;
              chan_set_done(dev);
         }
         break;

    case MT_WTM:
         if (uptr->POS == 0) {
             if (sim_tape_wrp(uptr)) {
                 uptr->STATUS |= WPROT|OPAT|TERMINATE;
                 uptr->STATUS &= ~BUSY;
                 mta_busy = 0;
                 uptr->STATUS &= FMASK;
                 chan_set_done(dev);
                 break;
             }
             uptr->POS ++;
             sim_activate(uptr, 500);
         } else {
             sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit);
             r = sim_tape_wrtmk(uptr);
             if (r != MTSE_OK)
                 uptr->STATUS |= OPAT;
             uptr->STATUS &= ~BUSY;
             uptr->STATUS |= TERMINATE;
             mta_busy = 0;
             chan_set_done(dev);
         }
         break;

    case MT_SKIPB:
         switch (uptr->POS ) {
         case 0:
              if (sim_tape_bot(uptr)) {
                  uptr->STATUS |= OPAT|TERMINATE;
                  uptr->STATUS &= ~BUSY;
                  mta_busy = 0;
                  chan_set_done(dev);
                  break;
              }
              uptr->POS ++;
              sim_activate(uptr, 500);
              break;
         case 1:
              sim_debug(DEBUG_DETAIL, dptr, "Backspace rec unit=%d ", unit);
              r = sim_tape_sprecr(uptr, &reclen);
              /* We don't set EOF on BSR */
              if (r == MTSE_TMK || r == MTSE_BOT) {
                  uptr->POS++;
                  sim_activate(uptr, 50);
              } else {
                  sim_debug(DEBUG_DETAIL, dptr, "%d \n", reclen);
                  sim_activate(uptr, 10 + (10 * reclen));
              }
              break;
         case 2:
              uptr->STATUS &= ~BUSY;
              uptr->STATUS |= TERMINATE|MARK;
              mta_busy = 0;
              chan_set_done(dev);
         }
         break;

    case MT_REW:
         if (uptr->POS == 0) {
             uptr->POS ++;
             sim_activate(uptr, 30000);
             mta_busy = 0;
         } else {
             sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d dev=%d\n", unit, dev);
             r = sim_tape_rewind(uptr);
             uptr->STATUS &= ~BUSY;
             uptr->STATUS |= TERMINATE;
             chan_set_done(dev);
         }
         break;

    }
    return SCPE_OK;
}


/* Reset */

t_stat mta_reset (DEVICE *dptr)
{
    UNIT  *uptr = dptr->units;
    uint32 unit;

    for (unit = 0; unit < dptr->numunits; unit++, uptr++) {
       uptr->CMD = 0;
       uptr->STATUS = 0;
       if ((uptr->flags & UNIT_ATT) == 0)
           uptr->STATUS |= OFFLINE;
       mta_busy = 0;
       chan_clr_done(GET_UADDR(dptr->flags)+unit);
    }
    return SCPE_OK;
}

/* Boot from given device */
t_stat
mta_boot(int32 unit_num, DEVICE * dptr)
{
    UNIT    *uptr = &dptr->units[unit_num];

    if ((uptr->flags & UNIT_ATT) == 0)
        return SCPE_UNATT;      /* attached? */

    uptr->ADDR = 0;
    uptr->CMD = MT_READ;
    uptr->STATUS = BUSY;
    uptr->POS = 0;
    CLR_BUF(uptr);
    loading = 1;
    mta_busy = 1;
    sim_activate (uptr, uptr->wait);
    return SCPE_OK;
}

t_stat
mta_attach(UNIT * uptr, CONST char *file)
{
    t_stat              r;
    DEVICE             *dptr = &mta_dev;
    int                 unit = (uptr - dptr->units);

    if ((r = sim_tape_attach_ex(uptr, file, 0, 0)) != SCPE_OK)
       return r;
    uptr->STATUS = ACCEPT;
    if (uptr->flags & UNIT_RO)
        uptr->flags |= MTUF_WLK;
    chan_set_done(unit + GET_UADDR(dptr->flags));
    return r;
}

t_stat
mta_detach(UNIT * uptr)
{
    uptr->STATUS |= OFFLINE;
    return sim_tape_detach(uptr);
}


t_stat mta_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cmta)
{
fprintf (st, "The Paper Tape Reader can be set to one of twp modes: 7P, or 7B.\n\n");
fprintf (st, "  mode \n");
fprintf (st, "  7P    Process even parity input tapes. \n");
fprintf (st, "  7B    Ignore parity of input data.\n");
fprintf (st, "The default mode is 7B.\n");
return SCPE_OK;
}

CONST char *mta_description (DEVICE *dptr)
{
    return "PTR";

}
#endif
