The B5500 simulator supports the following peripherals. Two CPUs with between 4K and 32K of memory. The second CPU can be enabled with "set cpu1 enable". "set cpu1 disable" disables the second CPU. Up to 4 floating IO channels. Individual channels can be enabled with "set io# enable", or "set io# disable". There are two card readers. The second reader is disabled by default. There is one Card Punch. The Card reader and Card Punch support the following options: set cr format= auto - will automatically determine the format based on the text it recieves. text Text based cards. Tabs are converted to the correct number of spaces. A record of ~raw octal will enter a binary card. ~eor will enter a 7/8/9 punch in column 1. ~eof will enter a 6/7/9 punch in column 1. ~eoi will enter a 6/7/8/9 punch in column 1. ~~ will enter a ~ as the first character. bin Binary Card format: Each record 160 characters. First characters 6789---- Second character 21012345 111 Top 4 bits of second character are 0. It is unlikely that any other format could look like this. bcd BCD Format: Each record variable length (80 chars or less). Record mark has bit 7 set. Bit 6 is even parity. Bits 5-0 are character. cbn CBN Format: Each record 160 charaters. First char has bit 7 set. Rest set to 0. Bit 6 is odd parity. Bit 5-0 of first character are top 6 bits of card. Bit 5-0 of second character are lower 6 bits of card. For punch format of auto if the card can't be converted to text it is output as a raw record. There are two line printers, the second one is disabled by default. The LP supports the option "set lp# linesperpage=#" which determines when the printer will force out a page break. There are up to 16 mag tape drives, the format is controlled by the standard simh format control for tapes. These are 6 bit tapes, 1 character per record with parity. Units 8-16 are disabled by default. There are up to two drum units DR0 and DR1. These can either be attached to a file or set to AUXMEM. Setting to AUXMEM causes them to exist only during the given simh run. Setting back to DRUM will clear whatever was stored on the drum. To enable use of DRUM on XV the following options should be turned on "DRA,DRB,CODEOLAY,DATAOLAY". MCP will then use the drum as a overlay device instead of the disk system. Disks can be attached to the various ESU's, ESU0-9 are on DKA by default, ESU10-19 are on DKB. If "set dk1 dfx" is set, then ESU10-19 are not used and the disks are shared by both DKA and DKB. To use more then 10 ESU's in a non shared mode, a new version of MCP must be created. MCP must be compiled with DFX option set to false. For MCP XV DKBNODFX must also be set to true. ESU units can be set to MODI or MODIB. MODIB will double the size of the drive. The DTC can be attached to a telnet port with "attach dtc #" to enable dialup access to the sim. The loader card for the card reader is: ~raw0104441100204231524012004000004444550211002041317700000000000024045303040243 00050000006501004131011041310055005500000062005042310000006600304231000000720010 42310000007675610165001002310010413100040107202500440106202533554061256520252265 20251765202514655355536117650000004401062025155522610165225572610465044101160500 4131 This card should be all in one line.
629 lines
22 KiB
C
629 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 u3 is device address */
|
||
/* in u4 is current buffer position */
|
||
/* in u5 Bits 30-16 of W */
|
||
#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 */
|
||
|
||
|
||
t_stat mt_srv(UNIT *);
|
||
t_stat mt_attach(UNIT *, 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);
|
||
|
||
/* Channel level activity */
|
||
uint8 mt_chan[NUM_CHAN];
|
||
|
||
uint16 mt_busy = 0; /* Busy bits */
|
||
|
||
/* 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
|
||
};
|
||
|
||
MTAB mt_mod[] = {
|
||
{MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL,
|
||
"Write ring in place"},
|
||
{MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", 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, NULL, 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 || 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->u5 & MT_LOADED))
|
||
return SCPE_UNATT;
|
||
|
||
/* Check if drive is ready to recieve a command */
|
||
if ((uptr->u5 & MT_BSY) != 0)
|
||
return SCPE_BUSY;
|
||
|
||
/* Determine actual command */
|
||
uptr->u5 &= ~(MT_RDY|MT_CHAN|MT_CMD|MT_BIN);
|
||
uptr->u5 |= chan;
|
||
if (cmd & URCSTA_BINARY)
|
||
uptr->u5 |= MT_BIN;
|
||
if (cmd & URCSTA_READ) {
|
||
if ((cmd & URCSTA_WC) && *wc == 0)
|
||
uptr->u5 |= MT_FSR;
|
||
else
|
||
uptr->u5 |= MT_RD;
|
||
} else {
|
||
/* Erase gap not supported on sim, treat as
|
||
write of null record */
|
||
if ((cmd & URCSTA_WC) && *wc == 0)
|
||
uptr->u5 |= MT_INT;
|
||
else
|
||
uptr->u5 |= MT_WR;
|
||
}
|
||
|
||
*wc = 0; /* So no overide occurs */
|
||
|
||
/* Convert command to correct type */
|
||
if (cmd & URCSTA_DIRECT)
|
||
uptr->u5 |= MT_BACK;
|
||
uptr->u6 = 0;
|
||
uptr->hwmark = -1;
|
||
sim_debug(DEBUG_CMD, &mt_dev, "Command %d %o %o\n\r", unit, uptr->u5, cmd);
|
||
if ((uptr->u5 & 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->u5 & MT_BOT) {
|
||
chan_set_blank(chan);
|
||
} else {
|
||
uptr->u5 &= ~MT_BOT;
|
||
uptr->u5 |= MT_EOT;
|
||
chan_set_eot(chan);
|
||
}
|
||
break;
|
||
|
||
case MTSE_TMK: /* tape mark */
|
||
sim_debug(DEBUG_EXP, dptr, "MARK ");
|
||
uptr->u5 &= ~(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->u5 &= ~MT_EOT;
|
||
uptr->u5 |= 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->u5 &= ~(MT_CMD|MT_BIN);
|
||
uptr->u5 |= 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->u5 & MT_CHAN;
|
||
int unit = uptr - mt_unit;
|
||
int cmd = uptr->u5 & 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->u5 & MT_LOADED) {
|
||
uptr->u5 &= ~MT_LOADED;
|
||
uptr->u5 |= MT_BSY|MT_RDY;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unit=%d Loaded\n\r", unit);
|
||
sim_activate(uptr, 50000);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
if (uptr->u5 & MT_BSY) {
|
||
uptr->u5 &= ~MT_BSY;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unit=%d Online\n\r", unit);
|
||
iostatus |= 1 << (uptr - mt_unit);
|
||
if (uptr->u5 & MT_IDLE)
|
||
sim_activate(uptr, 50000);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
if (uptr->u5 & MT_IDLE) {
|
||
uptr->u5 &= ~MT_IDLE;
|
||
if (uptr->u5 & MT_RDY) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unit=%d idling\n\r", unit);
|
||
return SCPE_OK;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unit=%d start %02o\n\r", unit, cmd);
|
||
}
|
||
|
||
switch (cmd) {
|
||
/* Handle interrogate */
|
||
case MT_INT:
|
||
if (sim_tape_wrp(uptr))
|
||
chan_set_wrp(chan);
|
||
uptr->u5 &= ~(MT_CMD|MT_BIN);
|
||
uptr->u5 |= MT_RDY;
|
||
chan_set_end(chan);
|
||
sim_debug(DEBUG_DETAIL, dptr, "Status\n\r");
|
||
return SCPE_OK;
|
||
|
||
case MT_RD: /* Read */
|
||
/* If at end of record, fill buffer */
|
||
if (uptr->hwmark == -1) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d %s ", unit,
|
||
(uptr->u5 & 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\r");
|
||
ch = 017;
|
||
(void)chan_write_char(chan, &ch, 1);
|
||
sim_activate(uptr, 4000);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "r=%d\n\r", r);
|
||
sim_activate(uptr, 5000);
|
||
}
|
||
return mt_error(uptr, chan, r, dptr);
|
||
} else {
|
||
uptr->u5 &= ~(MT_BOT|MT_EOT);
|
||
uptr->hwmark = reclen;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d chars\n\r", uptr->hwmark);
|
||
uptr->u6 = 0;
|
||
if ((uptr->u5 & 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->u6++] & 0177;
|
||
/* 00 characters are not transfered in BCD mode */
|
||
if (ch == 0) {
|
||
if (((uint32)uptr->u6) >= 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->u6) >= uptr->hwmark) ? 1 : 0)) {
|
||
sim_debug(DEBUG_DATA, dptr, "Read unit=%d %d EOR\n\r", unit,
|
||
uptr->hwmark-uptr->u6);
|
||
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\r",
|
||
unit, uptr->u6, ch);
|
||
sim_activate(uptr, HT);
|
||
}
|
||
return SCPE_OK;
|
||
|
||
case MT_RDBK: /* Read Backword */
|
||
/* If at end of record, fill buffer */
|
||
if (uptr->hwmark == -1) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read back unit=%d %s ", unit,
|
||
(uptr->u5 & 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\r");
|
||
ch = 017;
|
||
(void)chan_write_char(chan, &ch, 1);
|
||
sim_activate(uptr, 4000);
|
||
} else {
|
||
uptr->u5 |= MT_BSY;
|
||
sim_debug(DEBUG_DETAIL, dptr, "r=%d\n\r", r);
|
||
sim_activate(uptr, 100);
|
||
}
|
||
return mt_error(uptr, chan, r, dptr);
|
||
} else {
|
||
uptr->u5 &= ~(MT_BOT|MT_EOT);
|
||
uptr->hwmark = reclen;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d chars\n\r", uptr->hwmark);
|
||
uptr->u6 = uptr->hwmark;
|
||
if ((uptr->u5 & 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->u6] & 0177;
|
||
/* 00 characters are not transfered in BCD mode */
|
||
if (ch == 0) {
|
||
if (uptr->u6 <= 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->u6 > 0) ? 0 : 1)) {
|
||
sim_debug(DEBUG_DATA, dptr, "Read back unit=%d %d EOR\n\r",
|
||
unit, uptr->hwmark-uptr->u6);
|
||
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\r",
|
||
unit, uptr->u6, ch);
|
||
sim_activate(uptr, HT);
|
||
}
|
||
return SCPE_OK;
|
||
|
||
case MT_WR: /* Write */
|
||
/* Check if write protected */
|
||
if (uptr->u6 == 0 && sim_tape_wrp(uptr)) {
|
||
sim_activate(uptr, 100);
|
||
return mt_error(uptr, chan, MTSE_WRP, dptr);
|
||
}
|
||
if (chan_read_char(chan, &ch,
|
||
(uptr->u6 > BUFFSIZE) ? 1 : 0)) {
|
||
reclen = uptr->u6;
|
||
/* If no transfer, then either erase */
|
||
if (reclen == 0) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Erase\n\r");
|
||
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\r", unit);
|
||
r = sim_tape_wrtmk(uptr);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr,
|
||
"Write unit=%d Block %d %s chars\n\r", unit, reclen,
|
||
(uptr->u5 & MT_BIN)? "bin": "bcd");
|
||
r = sim_tape_wrrecf(uptr, &mt_buffer[chan][0], reclen);
|
||
}
|
||
uptr->u5 &= ~(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->u5 & MT_BIN))
|
||
ch ^= 0100;
|
||
/* Don't write out even parity zeros */
|
||
if (ch != 0)
|
||
mt_buffer[chan][uptr->u6++] = ch;
|
||
sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %03o\n\r",
|
||
unit, uptr->u6, ch);
|
||
uptr->hwmark = uptr->u6;
|
||
}
|
||
sim_activate(uptr, HT);
|
||
return SCPE_OK;
|
||
|
||
case MT_FSR: /* Space forward one record */
|
||
if (uptr->hwmark == -1) {
|
||
/* If at end of record, fill buffer */
|
||
sim_debug(DEBUG_DETAIL, dptr, "Space unit=%d ", unit);
|
||
if (sim_tape_eot(uptr)) {
|
||
uptr->u5 &= ~MT_BOT;
|
||
sim_debug(DEBUG_DETAIL, dptr, "EOT\r\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->u5 &= ~(MT_BOT|MT_EOT);
|
||
uptr->hwmark = reclen;
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d chars\n\r", 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 (uptr->hwmark == -1) {
|
||
/* 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\r");
|
||
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->u5 &= ~(MT_BOT|MT_EOT);
|
||
uptr->hwmark = reclen;
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d chars\n\r", 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\r", unit,
|
||
uptr->pos);
|
||
uptr->u5 &= ~(MT_CMD | MT_BIN | MT_IDLE | MT_RDY);
|
||
uptr->u5 |= MT_BSY|MT_RDY;
|
||
iostatus &= ~(1 << (uptr - mt_unit));
|
||
sim_activate(uptr, (uptr->pos/100) + 100);
|
||
r = sim_tape_rewind(uptr);
|
||
uptr->u5 &= ~MT_EOT;
|
||
uptr->u5 |= MT_BOT;
|
||
chan_set_end(chan);
|
||
return r;
|
||
}
|
||
return mt_error(uptr, chan, r, dptr);
|
||
}
|
||
|
||
|
||
t_stat
|
||
mt_attach(UNIT * uptr, char *file)
|
||
{
|
||
t_stat r;
|
||
|
||
if ((r = sim_tape_attach(uptr, file)) != SCPE_OK)
|
||
return r;
|
||
uptr->u5 |= MT_LOADED|MT_BOT;
|
||
sim_activate(uptr, 50000);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
mt_detach(UNIT * uptr)
|
||
{
|
||
uptr->u5 = 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++) {
|
||
mt_unit[i].dynflags = MT_DENS_556 << UNIT_V_DF_TAPE;
|
||
if ((mt_unit[i].flags & UNIT_ATT) == 0)
|
||
iostatus &= ~(1 << i);
|
||
else if (mt_unit[i].u5 & (MT_LOADED|MT_RDY)) {
|
||
iostatus |= 1 << i;
|
||
mt_unit[i].u5 &= ~(MT_LOADED);
|
||
mt_unit[i].u5 |= 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
|
||
|
||
|