1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-01-13 15:27:04 +00:00
rcornwell.sims/ICL1900/icl1900_mta.c
2020-10-12 23:27:46 -04:00

700 lines
24 KiB
C

/* 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 (uint32 dev, uint32 cmd);
void mta_nsi_status (uint32 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(uint32 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(uint32 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