blob: 7e3cd0cfdb77b32bf2ef99019e2a75a75aca453c [file] [log] [blame] [raw]
/* i7090_ht.c: ibm 7090 hypertape
Copyright (c) 2005-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.
Support for 7640 hypertape
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.
Hypertape orders appear to be of the following formats. Since there
is no documentation on them, I can going with what is shown in the
IBSYS sources.
BCD translated:
CTLW/CTLR 06u01 where u is unit numder
CTLW/CTLR 07u01 Backwords reading, where u is unit numder
CTL 06uoo01 Where u is unit number, and oo is order code
3x or 42
*/
#include "i7000_defs.h"
#include "sim_tape.h"
#ifdef NUM_DEVS_HT
#define BUFFSIZE (MAXMEMSIZE * CHARSPERWORD)
#define UNIT_HT(x) UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE|UNIT_S_CHAN(x)| \
UNIT_SELECT
#if defined(HTSIZE)
#undef HTSIZE
#endif
#define HTSIZE 31731000
/* in u3 is device address */
/* in u4 is current buffer position */
/* in u5 */
#define HT_CMDMSK 00000077 /* Command being run */
#define HT_NOTRDY 00000100 /* Devices is running a command */
#define HT_IDLE 00000200 /* Tape still in motion */
#define HT_MARK 00000400 /* Hit tape mark */
#define HT_EOR 00001000 /* Hit end of record */
#define HT_ERR 00002000 /* Device recieved error */
#define HT_BOT 00004000 /* Unit at begining of tape */
#define HT_EOT 00010000 /* Unit at end of tape */
#define HT_ATTN 00020000 /* Unit requests attntion */
#define HT_MOVE 00040000 /* Unit is moving to new record */
#define HT_WRITE 00100000 /* Were we writing */
#define HT_SNS 00200000 /* We were doing sense */
#define HT_CMD 00400000 /* We are fetching a command */
#define HT_PEND 01000000 /* Hold channel while command runs */
/* Hypertape commands */
#define HNOP 0x00 /* Nop */
#define HEOS 0x01 /* End of sequence */
#define HRLF 0x02 /* Reserved Light Off */
#define HRLN 0x03 /* Reserved Light On */
#define HCLF 0x04 /* Check light off? Not documented by might
be valid command */
#define HCLN 0x05 /* Check light on */
#define HSEL 0x06 /* Select */
#define HSBR 0x07 /* Select for backwards reading */
#define HCCR 0x28 /* Change cartridge and rewind */
#define HRWD 0x30 /* Rewind */
#define HRUN 0x31 /* Rewind and unload */
#define HERG 0x32 /* Erase long gap */
#define HWTM 0x33 /* Write tape mark */
#define HBSR 0x34 /* Backspace */
#define HBSF 0x35 /* Backspace file */
#define HSKR 0x36 /* Space */
#define HSKF 0x37 /* Space file */
#define HCHC 0x38 /* Change Cartridge */
#define HUNL 0x39 /* Unload Cartridge */
#define HFPN 0x42 /* File Protect On */
/* Hypertape sense word 1 codes */
/* 01234567 */
#define SEL_MASK 0x0F000000 /* Selected unit mask */
#define STAT_NOTRDY 0x80800000 /* Drive not ready */
#define PROG_NOTLOAD 0x40400000 /* *Drive not loaded */
#define PROG_FILEPROT 0x40200000 /* Drive write protected */
#define PROG_INVCODE 0x40080000 /* Invalid code */
#define PROG_BUSY 0x40040000 /* Drive Busy */
#define PROG_BOT 0x40020000 /* Drive at BOT BSR/BSF requested */
#define PROG_EOT 0x40010000 /* Drive at EOT forward motion requested. */
#define DATA_CHECK 0x20008000 /* *Error corrected */
#define DATA_PARITY 0x20004000 /* *Parity error */
#define DATA_CODECHK 0x20002000 /* *Code check */
#define DATA_ENVCHK 0x20001000 /* *Envelop error */
#define DATA_RESPONSE 0x20000800 /* Response check */
#define DATA_EXECSKEW 0x20000400 /* *Excessive skew check */
#define DATA_TRACKSKEW 0x20000200 /* *Track skew check */
#define EXP_MARK 0x10000080 /* Drive read a mark */
#define EXP_EWA 0x10000040 /* *Drive near EOT */
#define EXP_NODATA 0x10000020 /* *No data transfered */
#define READ_BSY 0x00000008 /* *Controller reading */
#define WRITE_BSY 0x00000004 /* *Controller writing */
#define BACK_MODE 0x00000002 /* *Backwards mode */
uint32 ht_cmd(UNIT *, uint16, uint16);
t_stat ht_srv(UNIT *);
t_stat htc_srv(UNIT *);
void ht_tape_cmd(DEVICE *, UNIT *);
t_stat ht_error(UNIT *, int, t_stat);
t_stat ht_boot(int32, DEVICE *);
t_stat ht_reset(DEVICE *);
t_stat ht_attach(UNIT *, CONST char *);
t_stat ht_detach(UNIT *);
t_stat ht_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *ht_description (DEVICE *dptr);
void ht_tape_posterr(UNIT * uptr, uint32 error);
/* One buffer per channel */
uint8 ht_unit[NUM_CHAN * 2]; /* Currently selected unit */
uint8 ht_buffer[NUM_DEVS_HT+1][BUFFSIZE];
int ht_cmdbuffer[NUM_CHAN]; /* Buffer holding command ids */
int ht_cmdcount[NUM_CHAN]; /* Count of command digits recieved */
uint32 ht_sense[NUM_CHAN * 2]; /* Sense data for unit */
UNIT hta_unit[] = {
/* Controller 1 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 0 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 1 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 2 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 3 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 4 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 5 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 6 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 7 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 8 */
{UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 9 */
{UDATA(&htc_srv, UNIT_S_CHAN(5)|UNIT_DISABLE|UNIT_DIS, 0)}, /* Controller */
#if NUM_DEVS_HT > 1
/* Controller 2 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 0 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 1 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 2 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 3 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 4 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 5 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 6 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 7 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 8 */
{UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 9 */
{UDATA(&htc_srv, UNIT_S_CHAN(8)|UNIT_DISABLE|UNIT_DIS, 0)}, /* Controller */
#endif
};
MTAB ht_mod[] = {
{MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, NULL,
"Write ring in place"},
{MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL, 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",
NULL, &sim_tape_show_capac, NULL,
"Set unit n capacity to arg MB (0 = unlimited)" },
{MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, &get_chan,
NULL, "Set Channel for device"},
#ifndef I7010 /* Not sure 7010 ever supported hypertapes */
{MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "SELECT", "SELECT",
&chan9_set_select, &chan9_get_select, NULL,
"Set unit number"},
#endif
{0}
};
DEVICE hta_dev = {
"HTA", hta_unit, NULL, ht_mod,
NUM_UNITS_HT + 1, 8, 15, 1, 8, 8,
NULL, NULL, &ht_reset, &ht_boot, &ht_attach, &ht_detach,
&ht_dib, DEV_BUF_NUM(0) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
NULL, NULL, &ht_help, NULL, NULL, &ht_description
};
#if NUM_DEVS_HT > 1
DEVICE htb_dev = {
"HTB", &hta_unit[NUM_UNITS_HT + 1], NULL, ht_mod,
NUM_UNITS_HT + 1, 8, 15, 1, 8, 8,
NULL, NULL, &ht_reset, &ht_boot, &ht_attach, &ht_detach,
&ht_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
NULL, NULL, &ht_help, NULL, NULL, &ht_description
};
#endif
uint32 ht_cmd(UNIT * uptr, uint16 cmd, uint16 dev)
{
DEVICE *dptr = find_dev_from_unit(uptr);
int chan = UNIT_G_CHAN(dptr->units[0].flags);
UNIT *u = &dptr->units[NUM_UNITS_HT];
/* Activate the device to start doing something */
ht_cmdbuffer[chan] = 0;
ht_cmdcount[chan] = 0;
sim_activate(u, 10);
return SCPE_OK;
}
t_stat htc_srv(UNIT * uptr)
{
DEVICE *dptr = find_dev_from_unit(uptr);
int chan = UNIT_G_CHAN(dptr->units[0].flags);
int sel;
int schan;
sel = (dptr->units[0].flags & UNIT_SELECT) ? 1 : 0;
if (sel != chan_test(chan, CTL_SEL))
return SCPE_OK;
schan = (chan * 2) + sel;
/* Drive is busy */
if (uptr->u5 & HT_NOTRDY) {
sim_debug(DEBUG_EXP, dptr, "Controller busy\n");
return SCPE_OK;
}
/* Handle sense on unit */
if (chan_test(chan, CTL_SNS)) {
uint8 ch = 0;
int eor = 0;
int i;
UNIT *up;
switch(ht_cmdcount[chan]) {
case 0: ch = (ht_sense[schan] >> 24) & 0xF;
uptr->u5 |= HT_SNS;
chan9_clear_error(chan, sel);
sim_debug(DEBUG_SNS, dptr, "Sense %08x\n", ht_sense[schan]);
break;
case 1: ch = ht_unit[schan];
break;
case 2: case 3: case 4: case 5: case 6: case 7:
ch = (ht_sense[schan] >> (4 * (7 - ht_cmdcount[chan]))) & 0xF;
break;
case 10:
eor = DEV_REOR;
/* Fall through */
case 9:
case 8:
i = 4 * (ht_cmdcount[chan] - 8);
up = &dptr->units[i];
ch = 0;
for (i = 3; i >= 0; i--, up++) {
if (up->u5 & HT_ATTN)
ch |= 1 << i;
}
break;
}
/* Fix out of align bit */
if (ch & 010)
ch ^= 030;
sim_debug(DEBUG_DATA, dptr, "sense %d %02o ", ht_cmdcount[chan], ch);
ht_cmdcount[chan]++;
switch(chan_write_char(chan, &ch, eor)) {
case TIME_ERROR:
case END_RECORD:
ht_sense[schan] = 0;
/* Fall through */
case DATA_OK:
uptr->u5 |= HT_SNS; /* So we catch disconnect */
if (eor) {
ht_sense[schan] = 0;
for (up = dptr->units, i = NUM_UNITS_HT; i >= 0; i--, up++)
up->u5 &= ~HT_ATTN;
}
break;
}
sim_activate(uptr, us_to_ticks(50));
return SCPE_OK;
}
/* If control, go collect it */
if (chan_test(chan, CTL_CNTL)) {
uptr->u5 |= HT_CMD;
ht_tape_cmd(dptr, uptr);
sim_activate(uptr, us_to_ticks(50));
return SCPE_OK;
}
/* Channel has disconnected, abort current operation. */
if (uptr->u5 & (HT_SNS|HT_CMD) && chan_stat(chan, DEV_DISCO)) {
uptr->u5 &= ~(HT_SNS|HT_CMD);
chan_clear(chan, DEV_WEOR|DEV_SEL);
sim_debug(DEBUG_CHAN, dptr, "control disconnecting\n");
}
return SCPE_OK;
}
t_stat ht_srv(UNIT * uptr)
{
int chan = UNIT_G_CHAN(uptr->flags);
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units);
UNIT *ctlr = &dptr->units[NUM_UNITS_HT];
int sel;
int schan;
t_stat r;
t_mtrlnt reclen;
sel = (uptr->flags & UNIT_SELECT) ? 1 : 0;
schan = (chan * 2) + sel;
/* Handle seeking */
if (uptr->wait > 0) {
uptr->wait--;
if (uptr->wait == 0) {
if (uptr->u5 & HT_PEND) {
chan_set(chan, DEV_REOR|CTL_END);
ctlr->u5 &= ~(HT_NOTRDY);
sim_activate(ctlr, us_to_ticks(50)); /* Schedule control to disco */
} else {
uptr->u5 |= HT_ATTN;
chan9_set_attn(chan, sel);
}
uptr->u5 &= ~(HT_PEND | HT_NOTRDY | HT_CMDMSK);
sim_debug(DEBUG_DETAIL, dptr, "%d Seek done\n", unit);
} else
sim_activate(uptr, us_to_ticks(1000));
return SCPE_OK;
}
if (sel != chan_test(chan, CTL_SEL))
return SCPE_OK;
/* Channel has disconnected, abort current operation. */
if ((uptr->u5 & HT_CMDMSK) == HSEL && chan_stat(chan, DEV_DISCO)) {
if (uptr->u5 & HT_WRITE) {
sim_debug(DEBUG_CMD, dptr,
"Write flush Block %d chars %d words\n", uptr->u6,
uptr->u6 / 6);
r = sim_tape_wrrecf(uptr, &ht_buffer[GET_DEV_BUF(dptr->flags)][0],
uptr->u6);
uptr->u5 &= ~HT_WRITE;
if (r != MTSE_OK) {
ht_error(uptr, schan, r);
chan9_set_attn(chan, sel);
}
uptr->u6 = 0;
}
uptr->u5 &= ~(HT_NOTRDY | HT_CMDMSK);
ctlr->u5 &= ~(HT_NOTRDY);
chan_clear(chan, DEV_WEOR|DEV_SEL);
sim_debug(DEBUG_CHAN, dptr, "disconnecting\n");
return SCPE_OK;
}
/* Handle writing of data */
if (chan_test(chan, CTL_WRITE) && (uptr->u5 & HT_CMDMSK) == HSEL) {
uint8 ch;
if (uptr->u6 == 0 && sim_tape_wrp(uptr)) {
ctlr->u5 &= ~(HT_NOTRDY);
ht_tape_posterr(uptr, PROG_FILEPROT);
sim_activate(uptr, us_to_ticks(50));
return SCPE_OK;
}
switch(chan_read_char(chan, &ch, 0)) {
case TIME_ERROR:
ht_tape_posterr(uptr, DATA_RESPONSE);
break;
case DATA_OK:
uptr->u5 |= HT_WRITE|HT_NOTRDY;
ctlr->u5 |= HT_NOTRDY;
ht_buffer[GET_DEV_BUF(dptr->flags)][uptr->u6++] = ch;
sim_debug(DEBUG_DATA, dptr, " write %d \n", ch);
if (uptr->u6 < BUFFSIZE)
break;
/* Overran tape buffer, give error */
ht_tape_posterr(uptr, DATA_TRACKSKEW);
case END_RECORD:
if (uptr->u6 != 0) {
sim_debug(DEBUG_CMD, dptr,
" Write Block %d chars %d words\n", uptr->u6,
uptr->u6 / 6);
r = sim_tape_wrrecf(uptr,
&ht_buffer[GET_DEV_BUF(dptr->flags)][0],
uptr->u6);
uptr->u5 &= ~HT_WRITE;
uptr->u6 = 0;
if (r != MTSE_OK) {
ht_error(uptr, schan, r);
chan9_set_error(chan, SNS_UEND);
}
}
chan_set(chan, DEV_REOR|CTL_END);
}
sim_activate(uptr, us_to_ticks(20));
return SCPE_OK;
}
/* Handle reading of data */
if (chan_test(chan, CTL_READ) && (uptr->u5 & HT_CMDMSK) == (HSEL)) {
uint8 ch;
if (uptr->u6 == 0) {
if (ht_sense[schan] & BACK_MODE)
r = sim_tape_rdrecr(uptr,
&ht_buffer[GET_DEV_BUF(dptr->flags)][0],
&reclen, BUFFSIZE);
else
r = sim_tape_rdrecf(uptr,
&ht_buffer[GET_DEV_BUF(dptr->flags)][0],
&reclen, BUFFSIZE);
if (r == MTSE_TMK)
sim_debug(DEBUG_CMD, dptr, "Read Mark\n");
else
sim_debug(DEBUG_CMD, dptr, "Read %d bytes\n", reclen);
/* Handle EOM special */
if (r == MTSE_EOM && (uptr->u5 & HT_EOT) == 0) {
uptr->u5 |= HT_EOT;
ht_sense[schan] |= EXP_NODATA;
chan_set(chan, DEV_REOR|CTL_END);
chan9_set_error(chan, SNS_UEND);
ctlr->u5 &= ~HT_NOTRDY;
sim_activate(uptr, us_to_ticks(20));
return SCPE_OK;
} else
/* Not read ok, return error */
if (r != MTSE_OK) {
ht_error(uptr, schan, r);
chan_set(chan, DEV_REOR|CTL_END);
chan9_set_error(chan, SNS_UEND);
ctlr->u5 &= ~HT_NOTRDY;
uptr->wait = 0;
sim_activate(uptr, us_to_ticks(50));
return SCPE_OK;
}
uptr->hwmark = reclen;
uptr->u5 |= HT_NOTRDY;
ctlr->u5 |= HT_NOTRDY;
}
if (uptr->u6 > (int32)uptr->hwmark) {
chan_set(chan, DEV_REOR|CTL_END);
sim_activate(uptr, us_to_ticks(50));
return SCPE_OK;
}
ch = ht_buffer[GET_DEV_BUF(dptr->flags)][uptr->u6++];
sim_debug(DEBUG_DATA, dptr, "data %02o\n", ch);
switch(chan_write_char(chan, &ch,
(uptr->u6 > (int32)uptr->hwmark)?DEV_REOR:0)) {
case TIME_ERROR:
/* Nop flag as timming error */
ht_tape_posterr(uptr, DATA_RESPONSE);
break;
case END_RECORD:
sim_debug(DEBUG_DATA, dptr, "eor\n");
chan_set(chan, DEV_REOR|CTL_END);
case DATA_OK:
break;
}
sim_activate(uptr, us_to_ticks(20));
return SCPE_OK;
}
/* If we have a command, keep scheduling us. */
if ((uptr->u5 & HT_CMDMSK) == (HSEL))
sim_activate(uptr, us_to_ticks(50));
return SCPE_OK;
}
/* Post a error on a given unit. */
void
ht_tape_posterr(UNIT * uptr, uint32 error)
{
int chan;
int schan;
int sel;
chan = UNIT_G_CHAN(uptr->flags);
sel = (uptr->flags & UNIT_SELECT) ? 1 : 0;
schan = (chan * 2) + sel;
uptr->u5 |= HT_ATTN;
ht_sense[schan] = error;
chan_set(chan, DEV_REOR|CTL_END);
chan9_set_attn(chan, sel);
if (error != 0)
chan9_set_error(chan, SNS_UEND);
}
/* Convert error codes to sense codes */
t_stat ht_error(UNIT * uptr, int schan, t_stat r)
{
switch (r) {
case MTSE_OK: /* no error */
break;
case MTSE_TMK: /* tape mark */
uptr->u5 |= HT_MARK;
ht_sense[schan] |= EXP_MARK;
break;
case MTSE_WRP: /* write protected */
uptr->u5 |= HT_ATTN;
ht_sense[schan] |= PROG_FILEPROT;
break;
case MTSE_UNATT: /* unattached */
uptr->u5 |= HT_ATTN;
ht_sense[schan] = PROG_NOTLOAD;
break;
case MTSE_IOERR: /* IO error */
case MTSE_INVRL: /* invalid rec lnt */
case MTSE_FMT: /* invalid format */
case MTSE_RECE: /* error in record */
uptr->u5 |= HT_ERR;
ht_sense[schan] |= DATA_CODECHK;
break;
case MTSE_BOT: /* beginning of tape */
uptr->u5 |= HT_BOT;
ht_sense[schan] |= PROG_BOT;
break;
case MTSE_EOM: /* end of medium */
uptr->u5 |= HT_EOT;
ht_sense[schan] |= PROG_EOT;
break;
default: /* Anything else if error */
ht_sense[schan] = PROG_INVCODE;
break;
}
return SCPE_OK;
}
/* Process command */
void
ht_tape_cmd(DEVICE * dptr, UNIT * uptr)
{
int chan = UNIT_G_CHAN(uptr->flags);
int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0;
int schan = (chan * 2) + sel;
UNIT *up;
uint8 c;
int i;
int t;
int cmd;
int unit;
t_stat r;
t_mtrlnt reclen;
/* Get information on unit */
/* Check if we have a command yet */
switch(chan_read_char(chan, &c, 0)) {
case END_RECORD:
case TIME_ERROR:
return;
case DATA_OK:
break;
}
c &= 017;
if (c == 012)
c = 0;
ht_cmdbuffer[chan] <<= 4;
ht_cmdbuffer[chan] |= c;
ht_cmdcount[chan]++;
/* If did not find end of sequence, request more */
if ((ht_cmdbuffer[chan] & 0xff) != HEOS) {
if (ht_cmdcount[chan] >= 8) {
/* command error */
ht_cmdcount[chan] = 0;
ht_sense[schan] = PROG_INVCODE;
ht_unit[schan] = 0;
chan_set(chan, DEV_REOR|SNS_UEND);
uptr->u5 &= ~HT_CMD;
}
return;
}
sim_debug(DEBUG_DETAIL, dptr, " cmd = %08x %d nybbles ",
ht_cmdbuffer[chan], ht_cmdcount[chan]);
/* See if we have a whole command string yet */
uptr->u5 &= ~HT_CMD;
cmd = 0xff;
unit = NUM_UNITS_HT + 1;
for (i = ht_cmdcount[chan] - 2; i >= 2; i -= 2) {
t = (ht_cmdbuffer[chan] >> (i * 4)) & 0xff;
switch (t) {
case HSEL: /* Select */
case HSBR: /* Select for backwards reading */
i--;
unit = (ht_cmdbuffer[chan] >> (i * 4)) & 0xf;
ht_sense[schan] = 0; /* Clear sense codes */
cmd = t;
break;
case HEOS: /* End of sequence */
break;
case HRLF: /* Reserved Light Off */
case HRLN: /* Reserved Light On */
case HCLF: /* Check light off */
case HCLN: /* Check light on */
case HNOP: /* Nop */
case HCCR: /* Change cartridge and rewind */
case HRWD: /* Rewind */
case HRUN: /* Rewind and unload */
case HERG: /* Erase long gap */
case HWTM: /* Write tape mark */
case HBSR: /* Backspace */
case HBSF: /* Backspace file */
case HSKR: /* Space */
case HSKF: /* Space file */
case HCHC: /* Change Cartridge */
case HUNL: /* Unload Cartridge */
case HFPN: /* File Protect On */
if (cmd != HSEL)
cmd = 0xff;
else
cmd = t;
break;
default:
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Invalid command %x\n",
cmd);
ht_sense[schan] = PROG_INVCODE;
chan_set(chan, DEV_REOR|CTL_END);
chan9_set_error(chan, SNS_UEND);
return;
}
}
/* Ok got a full command */
ht_cmdcount[chan] = 0;
/* Make sure we got a unit and command */
if (unit <= NUM_UNITS_HT)
ht_unit[schan] = unit;
else {
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr,
"Invalid unit %d cmd=%x\n", unit, cmd);
ht_sense[schan] = STAT_NOTRDY;
chan_set(chan, DEV_REOR|CTL_END);
chan9_set_error(chan, SNS_UEND);
return;
}
if (cmd == 0xff) {
/* command error */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Invalid command %x\n",
cmd);
ht_sense[schan] = PROG_INVCODE;
chan_set(chan, DEV_REOR|CTL_END);
chan9_set_error(chan, SNS_UEND);
return;
}
/* Find real device this command is for */
up = &dptr->units[unit];
if ((up->flags & UNIT_ATT) == 0) {
/* Not attached! */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Not ready %d cmd=%x\n",
unit, cmd);
ht_sense[schan] = STAT_NOTRDY;
chan_set(chan, DEV_REOR|CTL_END);
chan9_set_error(chan, SNS_UEND);
return;
}
if (up->u5 & HT_NOTRDY || up->wait > 0) {
/* Unit busy */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Busy unit %d cmd=%x\n", unit,
cmd);
ht_sense[schan] = PROG_BUSY;
chan_set(chan, DEV_REOR|CTL_END);
chan9_set_error(chan, SNS_UEND);
return;
}
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Execute unit %d cmd=%x ",
unit, cmd);
/* Ok, unit is ready and not in motion, set up to run command */
up->u5 &= ~(HT_PEND | HT_MARK | HT_ERR | HT_CMDMSK);
up->wait = 0;
up->u5 |= cmd;
ht_sense[schan] &= ~BACK_MODE;
r = MTSE_OK;
switch (cmd) {
case HSBR: /* Select for backwards reading */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "HSBR\n");
up->hwmark = -1;
up->u6 = 0;
ht_sense[schan] |= BACK_MODE;
up->u5 &= ~(HT_CMDMSK);
up->u5 |= HSEL;
chan_set(chan, DEV_REOR|DEV_SEL);
break;
case HSEL: /* Select */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "HSEL\n");
up->hwmark = -1;
up->u6 = 0;
chan_set(chan, DEV_REOR|DEV_SEL);
break;
case HRLF: /* Reserved Light Off */
case HRLN: /* Reserved Light On */
case HCLF: /* Check light off */
case HCLN: /* Check light on */
case HFPN: /* File Protect On (Nop for now ) */
case HEOS: /* End of sequence */
case HNOP: /* Nop */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "NOP\n");
up->u5 &= ~(HT_NOTRDY | HT_CMDMSK);
chan_set(chan, DEV_REOR|CTL_END);
return;
case HRWD: /* Rewind */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "REW\n");
if (up->u5 & HT_BOT) {
r = MTSE_OK;
up->wait = 1;
} else {
r = sim_tape_rewind(up);
up->u5 &= ~HT_EOT;
up->wait = 500;
}
up->u5 |= HT_BOT|HT_NOTRDY;
chan_set(chan, DEV_REOR|CTL_END);
break;
case HERG: /* Erase long gap */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "ERG\n");
if (sim_tape_wrp(up)) {
r = MTSE_WRP;
} else {
up->wait = 10;
up->u5 |= HT_PEND|HT_NOTRDY;
uptr->u5 |= HT_NOTRDY;
up->u5 &= ~HT_BOT;
}
break;
case HWTM: /* Write tape mark */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "WTM\n");
if (sim_tape_wrp(up)) {
r = MTSE_WRP;
} else {
r = sim_tape_wrtmk(up);
up->wait = 5;
up->u5 |= HT_PEND|HT_NOTRDY;
up->u5 &= ~(HT_BOT|HT_EOT);
uptr->u5 |= HT_NOTRDY;
}
break;
case HBSR: /* Backspace */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "BSR\n");
if (sim_tape_bot(up)) {
r = MTSE_BOT;
break;
}
r = sim_tape_sprecr(up, &reclen);
up->wait = reclen / 100;
up->wait += 2;
up->u5 |= HT_PEND|HT_NOTRDY;
up->u5 &= ~(HT_BOT|HT_EOT);
uptr->u5 |= HT_NOTRDY;
if (r == MTSE_TMK) {
r = MTSE_OK;
up->u5 |= HT_MARK;
}
if (sim_tape_bot(up))
up->u5 |= HT_BOT;
else
up->u5 &= ~HT_BOT;
break;
case HBSF: /* Backspace file */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "BSF\n");
if (sim_tape_bot(up)) {
r = MTSE_BOT;
break;
}
while ((r = sim_tape_sprecr(up, &reclen)) == MTSE_OK) {
up->wait += reclen;
}
up->wait /= 100;
up->wait += 2;
up->u5 |= HT_PEND|HT_NOTRDY;
up->u5 &= ~(HT_BOT|HT_EOT);
uptr->u5 |= HT_NOTRDY;
if (r == MTSE_TMK) {
r = MTSE_OK;
up->u5 |= HT_MARK;
}
if (sim_tape_bot(up))
up->u5 |= HT_BOT;
else
up->u5 &= ~HT_BOT;
break;
case HSKR: /* Space */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "SKR\n");
r = sim_tape_sprecf(up, &reclen);
up->u5 |= HT_PEND|HT_NOTRDY;
uptr->u5 |= HT_NOTRDY;
if (r == MTSE_TMK) {
r = MTSE_OK;
up->u5 |= HT_MARK;
}
up->wait = reclen / 100;
up->wait += 2;
up->u5 &= ~HT_BOT;
break;
case HSKF: /* Space file */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "SKF\n");
while ((r = sim_tape_sprecf(up, &reclen)) == MTSE_OK) {
up->wait += reclen;
}
up->wait /= 100;
up->wait += 2;
up->u5 |= HT_PEND|HT_NOTRDY;
uptr->u5 |= HT_NOTRDY;
if (r == MTSE_TMK) {
r = MTSE_OK;
up->u5 |= HT_MARK;
}
up->u5 &= ~HT_BOT;
break;
case HCCR: /* Change cartridge and rewind */
case HRUN: /* Rewind and unload */
case HCHC: /* Change Cartridge */
case HUNL: /* Unload Cartridge */
sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "RUN\n");
r = sim_tape_detach(up);
chan_set(chan, DEV_REOR|CTL_END);
up->u5 |= HT_NOTRDY;
up->wait = 100;
break;
}
if (r != MTSE_OK) {
ht_error(up, schan, r);
chan9_set_error(chan, SNS_UEND);
chan9_set_attn(chan, sel);
chan_set(chan, DEV_REOR|CTL_END);
up->u5 &= ~(HT_NOTRDY | HT_CMDMSK);
uptr->u5 &= ~HT_NOTRDY;
up->wait = 0;
} else if (up->u5 & HT_CMDMSK) {
sim_activate(up, us_to_ticks(1000));
} else {
chan9_set_attn(chan, sel);
}
return;
}
/* Boot Hypertape. Build boot card loader in memory and transfer to it */
t_stat
ht_boot(int unit_num, DEVICE * dptr)
{
#ifdef I7090
UNIT *uptr = &dptr->units[unit_num];
int chan = UNIT_G_CHAN(uptr->flags) - 1;
int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0;
int dev = uptr->u3;
int msk = (chan / 2) | ((chan & 1) << 11);
extern uint16 IC;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_UNATT; /* attached? */
if (dev == 0)
dev = 012;
/* Build Boot program in memory */
M[0] = 0000025000101LL; /* IOCD RSCQ,,21 */
M[1] = 0006000000001LL; /* TCOA * */
M[2] = 0002000000101LL; /* TRA RSCQ */
M[0101] = 0054000000113LL; /* RSCQ RSCC SMSQ Mod */
M[0101] |= ((t_uint64) (msk)) << 24;
M[0102] = 0064500000000LL; /* SCDQ SCDC 0 Mod */
M[0102] |= ((t_uint64) (msk)) << 24;
M[0103] = 0044100000000LL; /* LDI 0 */
M[0104] = 0405400001700LL; /* LFT 1700 */
M[0105] = 0002000000122LL; /* TRA HYP7 */
M[0106] = 0006000000102LL; /* TCOQ TCOC SCDQ Mod */
M[0106] |= ((t_uint64) (chan)) << 24;
M[0107] = 0002000000003LL; /* TRA 3 Enter IBSYS */
M[0110] = 0120600120112LL;
M[0110] |= ((t_uint64) (dev)) << 18;
M[0111] = 0120600030412LL; /*LDVCY DVCY Mod */
M[0111] |= ((t_uint64) (dev)) << 18;
M[0112] = 0010000000000LL; /* * */
M[0113] = 0700000000012LL; /* HYP6 SMS 10 */
M[0113] |= sel;
M[0114] = 0200000200110LL; /* CTLR *-4 */
M[0115] = 0400001000116LL; /* CPYP *+1,,1 */
M[0116] = 0000000000116LL; /* WTR * */
M[0117] = 0100000000115LL; /* TCH *-2 */
M[0120] = 0700000400113LL; /* SMS* HYP6 */
M[0121] = 0200000000111LL; /* CTL HYP6-2 */
M[0122] = 0076000000350LL; /* HYP7 RICC ** */
M[0122] |= ((t_uint64) (chan)) << 9;
M[0123] = 0054000000120LL; /* RSCC *-3 Mod */
M[0123] |= ((t_uint64) (msk)) << 24;
M[0124] = 0500000000000LL; /* CPYD 0,,0 */
M[0125] = 0340000000125LL; /* TWT * */
IC = 0101;
return SCPE_OK;
#else
return SCPE_NOFNC;
#endif
}
t_stat
ht_reset(DEVICE * dptr)
{
int i;
for (i = 0; i < NUM_CHAN; i++) {
ht_cmdbuffer[i] = ht_cmdcount[i] = 0;
ht_sense[i] = 0;
}
return SCPE_OK;
}
t_stat
ht_attach(UNIT * uptr, CONST char *file)
{
t_stat r;
if ((r = sim_tape_attach_ex(uptr, file, 0, 0)) != SCPE_OK)
return r;
uptr->u5 = HT_BOT /*|HT_ATTN */ ;
return SCPE_OK;
}
t_stat
ht_detach(UNIT * uptr)
{
uptr->u5 = 0;
if (uptr->flags & UNIT_DIS) return SCPE_OK;
return sim_tape_detach(uptr);
}
t_stat
ht_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "IBM 7340 Hypertape unit\n\n");
help_set_chan_type(st, dptr, "IBM 7340 Hypertape");
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
return SCPE_OK;
}
const char *
ht_description(DEVICE *dptr)
{
return "IBM 7340 Hypertape unit";
}
#endif