992 lines
35 KiB
C
992 lines
35 KiB
C
/* 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
|