Card reader handle EOF correctly. Line printer proper handling of paging. Magtape clean up debug message. Update 7010, 7070, 7080 for EOF handling.
1414 lines
49 KiB
C
1414 lines
49 KiB
C
/* i7090_mt.c: IBM 7090 Magnetic tape controller
|
||
|
||
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.
|
||
|
||
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 "i7000_defs.h"
|
||
#include "sim_tape.h"
|
||
|
||
#ifndef NUM_DEVS_MT
|
||
#define NUM_DEVS_MT 0
|
||
#endif
|
||
|
||
#if (NUM_DEVS_MT > 0) || defined(MT_CHANNEL_ZERO)
|
||
|
||
#define BUFFSIZE (MAXMEMSIZE * CHARSPERWORD)
|
||
#define UNIT_MT(x) UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | \
|
||
UNIT_S_CHAN(x)
|
||
#define MTUF_LDN (1 << MTUF_V_UF)
|
||
#define MTUF_ONLINE (1 << UNIT_V_UF_31)
|
||
|
||
|
||
/* in u3 is current frame of tape */
|
||
/* in u5 holds the commands */
|
||
#define MT_RDS 1
|
||
#define MT_RDSB 2
|
||
#define MT_WRS 3
|
||
#define MT_WRSB 4
|
||
#define MT_WEF 5
|
||
#define MT_BSR 6
|
||
#define MT_BSF 7
|
||
#define MT_REW 8
|
||
#define MT_SDN 9
|
||
#define MT_RUN 10
|
||
#define MT_SKIP 11 /* Do skip to end of record */
|
||
#define MT_WRITE 12 /* Actual transfer operation */
|
||
#define MT_SKR 13
|
||
#define MT_ERG 14
|
||
#define MT_RDB 15
|
||
#define MT_LREW 16 /* Low speed rewind */
|
||
#define MT_HREW 17 /* High speed rewind */
|
||
#define MT_CMDMSK 000037 /* Command being run */
|
||
#define MT_RDY 000040 /* Device is ready for command */
|
||
#define MT_IDLE 000100 /* Tape still in motion */
|
||
#define MT_MARK 000200 /* Hit tape mark */
|
||
#define MT_EOT 000400 /* At End Of Tape */
|
||
#define MT_RM 001000 /* Hit a record mark character */
|
||
#define MT_EOR 002000 /* Set EOR on next record */
|
||
#define MT_UNLOAD 004000 /* Unload when rewind done */
|
||
#define MT_EGAP 010000 /* Write extended gap on next write */
|
||
#define MT_LWR 020000 /* Last command was a write */
|
||
#define MT_CLRIND 040000 /* On I7010 flag for SKR to clear indicator */
|
||
|
||
/* u6 holds the current buffer position */
|
||
|
||
/* Flags for mt_chan */
|
||
#define MTC_SEL 0020 /* Controller executing read/write */
|
||
#define MTC_BSY 0040 /* Controller is busy - executing cmd */
|
||
#define MTC_UNIT 0017 /* device Channel is on */
|
||
|
||
/* Timing for tape */
|
||
#define IPS 75 /* Inches per second 75 or 112 */
|
||
#define HS_IPS 500 /* High speed rewind Inches per second */
|
||
|
||
#define LD 300
|
||
#define HD 150
|
||
|
||
#define LT_GAP_LEN ((3 * LD)/ 4) /* Gap length for low density */
|
||
#define HT_GAP_LEN ((3 * HD)/ 4) /* Gap length for high density */
|
||
#define LT (1000000/(LD * IPS)) /* Time per char low density */
|
||
#define HT (1000000/(HD * IPS)) /* Time per char high density */
|
||
#define LT_GAP_TIM (LT_GAP_LEN * LT) /* Time per char low density */
|
||
#define HT_GAP_TIM (HT_GAP_LEN * HT) /* Time per char high density */
|
||
|
||
/* Normal frame time */
|
||
#define T1 ((uptr->flags & MTUF_LDN) ?LT:HT)
|
||
#define T1_us us_to_ticks(T1)
|
||
/* Gap time */
|
||
#define T2 ((uptr->flags & MTUF_LDN) ?LT_GAP_TIM:HT_GAP_TIM)
|
||
#define T2_us us_to_ticks(T2)
|
||
/* Start time */
|
||
#define T3 (((uptr->flags & MTUF_LDN) ?LT_GAP_TIM:HT_GAP_TIM) + 500)
|
||
#define T3_us us_to_ticks(T3)
|
||
#define GAP_LEN ((uptr->flags & MTUF_LDN) ?LT_GAP_LEN:HT_GAP_LEN)
|
||
|
||
/* Definitions */
|
||
uint32 mt_cmd(UNIT *, uint16, uint16);
|
||
t_stat mt_srv(UNIT *);
|
||
t_stat mt_boot(int32, DEVICE *);
|
||
void mt_ini(UNIT *, t_bool);
|
||
t_stat mt_reset(DEVICE *);
|
||
t_stat mt_attach(UNIT *, CONST char *);
|
||
t_stat mt_detach(UNIT *);
|
||
t_stat mt_rew(UNIT * uptr, int32 val, CONST char *cptr,
|
||
void *desc);
|
||
t_stat mt_tape_density(UNIT * uptr, int32 val, CONST char *cptr,
|
||
void *desc);
|
||
t_stat mt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
|
||
const char *cptr);
|
||
const char *mt_description (DEVICE *dptr);
|
||
extern t_stat chan_boot(int32, DEVICE *);
|
||
#ifdef I7010
|
||
extern uint8 chan_io_status[NUM_CHAN]; /* Channel status */
|
||
#endif
|
||
|
||
#ifdef MT_CHANNEL_ZERO
|
||
#define NUM_DEVS (NUM_DEVS_MT + 1)
|
||
#else
|
||
#define NUM_DEVS (NUM_DEVS_MT)
|
||
#endif
|
||
|
||
/* Channel level activity */
|
||
uint8 mt_chan[NUM_CHAN];
|
||
|
||
/* One buffer per channel */
|
||
uint8 mt_buffer[NUM_DEVS][BUFFSIZE];
|
||
|
||
UNIT mta_unit[] = {
|
||
/* Controller 1 */
|
||
#if (NUM_DEVS_MT > 0)
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 7 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 8 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 9 */
|
||
#if (NUM_DEVS_MT > 1)
|
||
/* Controller 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 7 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 8 */
|
||
{UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 9 */
|
||
#if (NUM_DEVS_MT > 2)
|
||
/* Controller 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 7 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 8 */
|
||
{UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 9 */
|
||
#if (NUM_DEVS_MT > 3)
|
||
/* Controller 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 7 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 8 */
|
||
{UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 9 */
|
||
#if (NUM_DEVS_MT > 4)
|
||
/* Controller 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 7 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 8 */
|
||
{UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 9 */
|
||
#if (NUM_DEVS_MT > 5)
|
||
/* Controller 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 7 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 8 */
|
||
{UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 9 */
|
||
#endif
|
||
#endif
|
||
#endif
|
||
#endif
|
||
#endif
|
||
#endif
|
||
#ifdef MT_CHANNEL_ZERO
|
||
/* Controller 7 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 7 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 8 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 9 */
|
||
#endif
|
||
};
|
||
|
||
MTAB mt_mod[] = {
|
||
{ MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED",
|
||
&set_writelock, &show_writelock, NULL, "Write ring in place" },
|
||
{ MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED",
|
||
&set_writelock, NULL, NULL, "no Write ring in place" },
|
||
{MTUF_LDN, 0, "high density", "HIGH", &mt_tape_density, NULL, NULL,
|
||
"556 BPI"},
|
||
{MTUF_LDN, MTUF_LDN, "low density", "LOW", &mt_tape_density, NULL, NULL,
|
||
"200 BPI"},
|
||
#ifdef I7090
|
||
{MTUF_ONLINE, 0, "offline", "OFFLINE", NULL, NULL, NULL,
|
||
"Tape offline"},
|
||
{MTUF_ONLINE, MTUF_ONLINE, "online", "ONLINE", NULL, NULL, NULL,
|
||
"Tape Online"},
|
||
#endif
|
||
{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, NULL, "REWIND",
|
||
&mt_rew, NULL, NULL, "Rewind tape"
|
||
},
|
||
#ifdef I7090
|
||
{MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, &get_chan,
|
||
NULL, "Device Channel"},
|
||
#endif
|
||
{0}
|
||
};
|
||
|
||
#ifdef MT_CHANNEL_ZERO
|
||
DEVICE mtz_dev = {
|
||
"MT", &mta_unit[NUM_DEVS_MT * 10], NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
|
||
&mt_dib, DEV_BUF_NUM(NUM_DEVS_MT) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
|
||
NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
||
};
|
||
#endif
|
||
|
||
#if (NUM_DEVS_MT > 0)
|
||
DEVICE mta_dev = {
|
||
"MTA", mta_unit, NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
|
||
&mt_dib, DEV_BUF_NUM(0) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
|
||
NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
||
};
|
||
|
||
#if (NUM_DEVS_MT > 1)
|
||
DEVICE mtb_dev = {
|
||
"MTB", &mta_unit[10], NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
|
||
&mt_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
|
||
NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
||
};
|
||
|
||
#if (NUM_DEVS_MT > 2)
|
||
DEVICE mtc_dev = {
|
||
"MTC", &mta_unit[20], NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
|
||
&mt_dib, DEV_BUF_NUM(2) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
|
||
NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
||
};
|
||
|
||
#if (NUM_DEVS_MT > 3)
|
||
DEVICE mtd_dev = {
|
||
"MTD", &mta_unit[30], NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 36,
|
||
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
|
||
&mt_dib, DEV_BUF_NUM(3) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
|
||
NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
||
};
|
||
|
||
#if (NUM_DEVS_MT > 4)
|
||
DEVICE mte_dev = {
|
||
"MTE", &mta_unit[40], NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
|
||
&mt_dib, DEV_BUF_NUM(4) | DEV_DIS | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
|
||
NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
||
};
|
||
|
||
#if (NUM_DEVS_MT > 5)
|
||
DEVICE mtf_dev = {
|
||
"MTF", &mta_unit[50], NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
|
||
&mt_dib, DEV_BUF_NUM(5) | DEV_DIS | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
|
||
NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
||
};
|
||
#endif
|
||
#endif
|
||
#endif
|
||
#endif
|
||
#endif
|
||
#endif
|
||
|
||
|
||
uint8 parity_table[64] = {
|
||
/* 0 1 2 3 4 5 6 7 */
|
||
0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
|
||
0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
|
||
0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
|
||
0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
|
||
0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
|
||
0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
|
||
0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
|
||
0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000
|
||
};
|
||
|
||
/* Rewind tape drive */
|
||
t_stat
|
||
mt_rew(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
/* If drive is offline or not attached return not ready */
|
||
if ((uptr->flags & (UNIT_ATT | MTUF_ONLINE)) == 0)
|
||
return SCPE_NOATT;
|
||
/* Check if drive is ready to recieve a command */
|
||
if ((uptr->u5 & MT_RDY) == 0)
|
||
return STOP_IOCHECK;
|
||
return sim_tape_rewind(uptr);
|
||
}
|
||
|
||
/* Start off a mag tape command */
|
||
uint32 mt_cmd(UNIT * uptr, uint16 cmd, uint16 dev)
|
||
{
|
||
int chan = UNIT_G_CHAN(uptr->flags);
|
||
DEVICE *dptr = find_dev_from_unit(uptr);
|
||
int time = us_to_ticks(100);
|
||
int unit = dev & 017;
|
||
|
||
unit -= mt_dib.addr & 017; /* Adjust to origin zero */
|
||
if (unit == 10)
|
||
unit = 0;
|
||
/* Make sure valid drive number */
|
||
if (unit > NUM_UNITS_MT || unit < 0)
|
||
return SCPE_NODEV;
|
||
uptr += unit;
|
||
/* If unit disabled return error */
|
||
if (uptr->flags & UNIT_DIS) {
|
||
return SCPE_NODEV;
|
||
}
|
||
|
||
/* Check status of the drive */
|
||
|
||
/* Can't do nothing if controller is busy */
|
||
if (mt_chan[chan] & MTC_BSY) {
|
||
return SCPE_BUSY;
|
||
}
|
||
/* If drive is offline or not attached return not ready */
|
||
if ((uptr->flags & (UNIT_ATT | MTUF_ONLINE)) !=
|
||
(UNIT_ATT | MTUF_ONLINE)) {
|
||
sim_messagef(SCPE_OK, "Attempt to access offline unit %s%d\n\r", dptr->name, unit);
|
||
return SCPE_IOERR;
|
||
}
|
||
/* Check if drive is ready to recieve a command */
|
||
if ((uptr->u5 & MT_RDY) == 0) {
|
||
/* Return indication if not ready and doing TRS */
|
||
if (cmd == IO_TRS) {
|
||
return SCPE_IOERR;
|
||
} else {
|
||
return SCPE_BUSY;
|
||
}
|
||
}
|
||
uptr->u5 &= ~(MT_CMDMSK | MT_RDY);
|
||
time = us_to_ticks(12000);
|
||
if ((uptr->u5 & MT_IDLE) == 0)
|
||
time = us_to_ticks(15000);
|
||
switch (cmd) {
|
||
case IO_RDS:
|
||
if (sim_tape_bot(uptr))
|
||
time = us_to_ticks(21000);
|
||
if (mt_chan[chan] & MTC_SEL) {
|
||
uptr->u5 |= MT_RDY;
|
||
return SCPE_BUSY;
|
||
}
|
||
|
||
#ifdef I701
|
||
uptr->u5 |= MT_RDSB;
|
||
#else
|
||
if (dev & 020)
|
||
uptr->u5 |= MT_RDSB;
|
||
else
|
||
uptr->u5 |= MT_RDS;
|
||
#endif
|
||
chan_set_sel(chan, 0);
|
||
chan_clear_status(chan);
|
||
mt_chan[chan] = MTC_BSY | MTC_SEL | unit;
|
||
uptr->u5 &= ~(MT_RM|MT_EOR|MT_EGAP);
|
||
uptr->u6 = -1;
|
||
uptr->hwmark = -1;
|
||
#if I7010 | I7080
|
||
chan_set(chan, STA_TWAIT);
|
||
#endif
|
||
sim_debug(DEBUG_CMD, dptr, "RDS %s unit=%d %d\n",
|
||
((uptr->u5 & MT_CMDMSK) == MT_RDS) ? "BCD" : "Binary",
|
||
unit, dev);
|
||
break;
|
||
|
||
case IO_WRS:
|
||
if (sim_tape_bot(uptr))
|
||
time = us_to_ticks(40000);
|
||
if (mt_chan[chan] & MTC_SEL) {
|
||
uptr->u5 |= MT_RDY;
|
||
return SCPE_BUSY;
|
||
}
|
||
if (sim_tape_wrp(uptr)) {
|
||
sim_debug(DEBUG_EXP, dptr,
|
||
"WRS %d attempted on locked tape\n", unit);
|
||
uptr->u5 |= MT_RDY;
|
||
return SCPE_IOERR;
|
||
}
|
||
#ifdef I701
|
||
uptr->u5 |= MT_WRSB;
|
||
#else
|
||
if (dev & 020)
|
||
uptr->u5 |= MT_WRSB;
|
||
else
|
||
uptr->u5 |= MT_WRS;
|
||
#endif
|
||
time += T2_us;
|
||
uptr->u6 = 0;
|
||
uptr->hwmark = 0;
|
||
chan_set_sel(chan, 1);
|
||
chan_clear_status(chan);
|
||
mt_chan[chan] = MTC_BSY | MTC_SEL | unit;
|
||
uptr->u5 &= ~(MT_MARK | MT_EOT);
|
||
#if I7010 | I7080
|
||
chan_set(chan, STA_TWAIT);
|
||
#endif
|
||
sim_debug(DEBUG_CMD, dptr, "WRS %s unit=%d %d\n",
|
||
((uptr->u5 & MT_CMDMSK) == MT_WRS) ? "BCD" : "Binary",
|
||
unit, dev);
|
||
break;
|
||
|
||
case IO_RDB:
|
||
if (mt_chan[chan] & MTC_SEL) {
|
||
uptr->u5 |= MT_RDY;
|
||
return SCPE_BUSY;
|
||
}
|
||
|
||
uptr->u5 |= MT_RDB;
|
||
chan_set_sel(chan, 0);
|
||
chan_clear_status(chan);
|
||
mt_chan[chan] = MTC_BSY | MTC_SEL | unit;
|
||
uptr->u5 &= ~(MT_RM|MT_EOR|MT_EGAP);
|
||
uptr->u6 = -1;
|
||
uptr->hwmark = -1;
|
||
#if I7010 | I7080
|
||
chan_set(chan, STA_TWAIT);
|
||
#endif
|
||
sim_debug(DEBUG_CMD, dptr, "RDB unit=%d %d\n", unit, dev);
|
||
break;
|
||
|
||
case IO_WEF:
|
||
if (sim_tape_bot(uptr))
|
||
time = us_to_ticks(40000);
|
||
uptr->u5 &= ~(MT_EOT|MT_MARK);
|
||
if (sim_tape_wrp(uptr)) {
|
||
sim_debug(DEBUG_EXP, dptr,
|
||
"WRS %d attempted on locked tape\n", unit);
|
||
uptr->u5 |= MT_RDY;
|
||
return SCPE_IOERR;
|
||
}
|
||
uptr->u5 |= MT_WEF;
|
||
#if I7010
|
||
chan_set_sel(chan, 1);
|
||
chan_clear_status(chan);
|
||
mt_chan[chan] = MTC_BSY | MTC_SEL | unit;
|
||
chan_set(chan, STA_TWAIT);
|
||
#else
|
||
mt_chan[chan] = MTC_BSY;
|
||
#endif
|
||
sim_debug(DEBUG_CMD, dptr, "WEF unit=%d\n", unit);
|
||
break;
|
||
|
||
case IO_BSR:
|
||
uptr->u5 &= ~(MT_MARK);
|
||
/* Check if at load point, quick return if so */
|
||
if (sim_tape_bot(uptr)) {
|
||
sim_debug(DEBUG_CMD, dptr, "BSR unit=%d at BOT\n", unit);
|
||
uptr->u5 |= MT_RDY;
|
||
uptr->u3 = 0;
|
||
chan_set(chan, CHS_BOT);
|
||
return SCPE_OK;
|
||
}
|
||
uptr->u5 |= MT_BSR;
|
||
mt_chan[chan] = MTC_BSY;
|
||
sim_debug(DEBUG_CMD, dptr, "BSR unit=%d\n", unit);
|
||
break;
|
||
|
||
case IO_BSF:
|
||
uptr->u5 &= ~(MT_MARK);
|
||
/* Check if at load point, quick return if so */
|
||
if (sim_tape_bot(uptr)) {
|
||
sim_debug(DEBUG_CMD, dptr, "BSF unit=%d at BOT\n", unit);
|
||
uptr->u5 |= MT_RDY;
|
||
uptr->u3 = 0;
|
||
chan_set(chan, CHS_BOT);
|
||
return SCPE_OK;
|
||
}
|
||
uptr->u5 |= MT_BSF;
|
||
mt_chan[chan] = MTC_BSY;
|
||
sim_debug(DEBUG_CMD, dptr, "BSF unit=%d\n", unit);
|
||
break;
|
||
|
||
case IO_SKR:
|
||
if (sim_tape_bot(uptr))
|
||
time = us_to_ticks(21000);
|
||
uptr->u5 &= ~(MT_MARK|MT_EGAP);
|
||
uptr->u5 |= MT_SKR;
|
||
mt_chan[chan] = MTC_BSY;
|
||
sim_debug(DEBUG_CMD, dptr, "SKR unit=%d\n", unit);
|
||
break;
|
||
|
||
case IO_ERG:
|
||
sim_debug(DEBUG_CMD, dptr, "ERG unit=%d\n", unit);
|
||
#ifdef I7080
|
||
uptr->u5 &= ~(MT_MARK);
|
||
uptr->u5 |= MT_ERG;
|
||
mt_chan[chan] = MTC_BSY;
|
||
chan_set(chan, STA_TWAIT);
|
||
break;
|
||
#else
|
||
uptr->u5 |= MT_EGAP|MT_RDY; /* Command is quick */
|
||
return SCPE_OK;
|
||
#endif
|
||
|
||
case IO_REW:
|
||
uptr->u5 &= ~(MT_EOT|MT_MARK|MT_EGAP);
|
||
/* Check if at load point, quick return if so */
|
||
if (sim_tape_bot(uptr)) {
|
||
sim_debug(DEBUG_CMD, dptr, "REW unit=%d at BOT\n", unit);
|
||
uptr->u5 |= MT_RDY;
|
||
uptr->u3 = 0;
|
||
return SCPE_OK;
|
||
}
|
||
time = 1000;
|
||
uptr->u5 |= MT_REW;
|
||
mt_chan[chan] = MTC_BSY;
|
||
sim_debug(DEBUG_CMD, dptr, "REW unit=%d\n", unit);
|
||
sim_cancel(uptr);
|
||
sim_activate(uptr, time);
|
||
return SCPE_OK;
|
||
|
||
case IO_RUN:
|
||
uptr->u5 &= ~(MT_EOT|MT_MARK|MT_EGAP);
|
||
chan_clear_status(chan);
|
||
uptr->u5 |= MT_RUN;
|
||
mt_chan[chan] = MTC_BSY;
|
||
time = 1000;
|
||
sim_debug(DEBUG_CMD, dptr, "RUN unit=%d\n", unit);
|
||
sim_cancel(uptr);
|
||
sim_activate(uptr, time);
|
||
return SCPE_OK;
|
||
|
||
case IO_SDL:
|
||
uptr->u5 |= MT_RDY; /* Command is quick */
|
||
uptr->flags |= MTUF_LDN;
|
||
sim_tape_set_dens (uptr, MT_DENS_200, NULL, NULL);
|
||
sim_debug(DEBUG_CMD, dptr, "SDN unit=%d low\n", unit);
|
||
return SCPE_OK;
|
||
|
||
case IO_SDH:
|
||
uptr->u5 |= MT_RDY; /* Command is quick */
|
||
uptr->flags &= ~MTUF_LDN;
|
||
sim_tape_set_dens (uptr, MT_DENS_556, NULL, NULL);
|
||
sim_debug(DEBUG_CMD, dptr, "SDN unit=%d high\n", unit);
|
||
return SCPE_OK;
|
||
|
||
case IO_DRS:
|
||
uptr->flags &= ~MTUF_ONLINE;
|
||
uptr->u5 |= MT_RDY; /* Command is quick */
|
||
sim_debug(DEBUG_CMD, dptr, "DRS unit=%d\n", unit);
|
||
return SCPE_OK;
|
||
|
||
case IO_TRS:
|
||
uptr->u5 |= MT_RDY; /* Get here we are ready */
|
||
sim_debug(DEBUG_CMD, dptr, "TRS unit=%d\n", unit);
|
||
return SCPE_OK;
|
||
}
|
||
sim_cancel(uptr);
|
||
sim_activate(uptr, time);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
#if I7090 | I704 | I701
|
||
/* Read a word from tape, used during boot read */
|
||
int
|
||
mt_read_buff(UNIT * uptr, int cmd, DEVICE * dptr, t_uint64 *word)
|
||
{
|
||
int chan = UNIT_G_CHAN(uptr->flags);
|
||
int bufnum = GET_DEV_BUF(dptr->flags);
|
||
int i;
|
||
uint8 ch;
|
||
int mode = 0;
|
||
int mark = 1;
|
||
int parity = 0;
|
||
|
||
uptr->u5 &= ~MT_MARK;
|
||
if (cmd == MT_RDS)
|
||
mode = 0100;
|
||
|
||
*word = 0;
|
||
for(i = CHARSPERWORD-1; i >= 0 && uptr->u6 < (int32)uptr->hwmark; i--) {
|
||
ch = mt_buffer[bufnum][uptr->u6++];
|
||
/* Do BCD translation */
|
||
if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) {
|
||
parity = 1;
|
||
}
|
||
ch &= 077;
|
||
/* Not needed on decimal machines */
|
||
if (mode) {
|
||
/* Map BCD to internal format */
|
||
ch ^= (ch & 020) << 1;
|
||
if (ch == 012)
|
||
ch = 0;
|
||
if (ch == 017 && mark) {
|
||
chan_set_error(chan); /* Force CRC error. */
|
||
ch = 0;
|
||
mark = 0;
|
||
uptr->u6++; /* Skip next character */
|
||
i--;
|
||
}
|
||
}
|
||
if (i >= 0)
|
||
*word |= ((t_uint64) ch) << (6 * i);
|
||
}
|
||
|
||
if (parity) {
|
||
chan_set_error(chan); /* Force redundency error */
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
#endif
|
||
|
||
/* 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 */
|
||
break;
|
||
|
||
case MTSE_TMK: /* tape mark */
|
||
sim_debug(DEBUG_EXP, dptr, "MARK ");
|
||
chan_set_eof(chan);
|
||
break;
|
||
|
||
case MTSE_WRP: /* write protected */
|
||
case MTSE_UNATT: /* unattached */
|
||
sim_debug(DEBUG_EXP, dptr, "ATTENTION %d ", r);
|
||
chan_set_attn(chan);
|
||
break;
|
||
|
||
case MTSE_IOERR: /* IO error */
|
||
case MTSE_FMT: /* invalid format */
|
||
case MTSE_RECE: /* error in record */
|
||
chan_set_error(chan); /* Force redundency error */
|
||
chan_set_attn(chan); /* Set error */
|
||
sim_debug(DEBUG_EXP, dptr, "ERROR %d ", r);
|
||
break;
|
||
case MTSE_BOT: /* beginning of tape */
|
||
chan_set(chan, CHS_BOT); /* Set flag */
|
||
sim_debug(DEBUG_EXP, dptr, "BOT ");
|
||
break;
|
||
case MTSE_INVRL: /* invalid rec lnt */
|
||
case MTSE_EOM: /* end of medium */
|
||
uptr->u5 |= MT_EOT;
|
||
sim_debug(DEBUG_EXP, dptr, "EOT ");
|
||
#ifdef I7010
|
||
chan_set_attn(chan); /* Set error */
|
||
#endif
|
||
break;
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Handle processing of tape requests. */
|
||
t_stat mt_srv(UNIT * uptr)
|
||
{
|
||
int chan = UNIT_G_CHAN(uptr->flags);
|
||
DEVICE *dptr = find_dev_from_unit(uptr);
|
||
int unit = (uptr - dptr->units) & MTC_UNIT;
|
||
int cmd = uptr->u5 & MT_CMDMSK;
|
||
int bufnum = GET_DEV_BUF(dptr->flags);
|
||
t_mtrlnt reclen;
|
||
t_stat r = SCPE_ARG; /* Force error if not set */
|
||
uint8 ch;
|
||
int mode = 0;
|
||
#ifdef I7010
|
||
extern uint8 astmode;
|
||
#endif
|
||
|
||
/* Call channel proccess to make sure data is ready */
|
||
chan_proc();
|
||
|
||
/* Channel has disconnected, abort current read. */
|
||
if ((mt_chan[chan] & 037) == (MTC_SEL | unit) &&
|
||
chan_test(chan, DEV_DISCO)) {
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
reclen = uptr->hwmark;
|
||
if (cmd == MT_WRS || cmd == MT_WRSB) {
|
||
if (uptr->u6 > 0) {
|
||
uptr->u3 += GAP_LEN;
|
||
sim_debug(DEBUG_DETAIL, dptr,
|
||
"Write flush unit=%d %s Block %d chars\n",
|
||
unit, (cmd == MT_WRS) ? "BCD" : "Binary", reclen);
|
||
r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen);
|
||
mt_error(uptr, chan, r, dptr); /* Record errors */
|
||
}
|
||
} else if (cmd == MT_RDS || cmd == MT_RDSB) {
|
||
sim_debug(DEBUG_DETAIL, dptr,
|
||
"Read flush unit=%d %s at %d Block %d chars\n",
|
||
unit, (cmd == MT_RDS) ? "BCD" : "Binary", uptr->u6, reclen);
|
||
/* Keep moving until end of block */
|
||
if (uptr->u6 < (int32)reclen ) {
|
||
reclen -= uptr->u6;
|
||
uptr->u3 += reclen;
|
||
uptr->u5 |= MT_SKIP|MT_IDLE;
|
||
uptr->u6 = 0;
|
||
uptr->hwmark = 0;
|
||
chan_clear(chan, DEV_WEOR );
|
||
sim_activate(uptr, reclen * T1_us);
|
||
return SCPE_OK;
|
||
} else {
|
||
#ifndef I7010
|
||
if (uptr->u5 & MT_MARK) {
|
||
/* We hit tapemark, Back up so next read hits it */
|
||
/* Or write starts just before it */
|
||
/* This is due to SIMH returning mark after read */
|
||
(void) sim_tape_sprecr(uptr, &reclen);
|
||
uptr->u5 &= ~MT_MARK;
|
||
uptr->u3 -= GAP_LEN + reclen;
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
/* Allow time for tape to be restarted, before stop */
|
||
sim_activate(uptr, us_to_ticks(500));
|
||
uptr->u6 = 0;
|
||
uptr->hwmark = 0;
|
||
sim_debug(DEBUG_CHAN, dptr, "Disconnect unit=%d\n", unit);
|
||
uptr->u5 |= MT_IDLE|MT_RDY;
|
||
mt_chan[chan] = 0;
|
||
chan_clear(chan, DEV_DISCO | DEV_WEOR | DEV_SEL);
|
||
#if I7010 | I7080
|
||
chan_clear(chan, STA_TWAIT);
|
||
#endif
|
||
return SCPE_OK;
|
||
}
|
||
|
||
uptr->u5 &= ~MT_IDLE;
|
||
switch (cmd) {
|
||
case 0: /* No command, stop tape */
|
||
uptr->u5 |= MT_RDY; /* Ready since command is done */
|
||
mt_chan[chan] &= ~MTC_BSY;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Idle unit=%d\n", unit);
|
||
return SCPE_OK;
|
||
|
||
case MT_SKIP: /* Record skip done, enable tape drive */
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
uptr->u5 |= MT_RDY | MT_IDLE;
|
||
#if I7090 | I704 | I701
|
||
chan_clear(chan, DEV_SEL);
|
||
#else
|
||
chan_clear(chan, DEV_SEL|STA_TWAIT);
|
||
#endif
|
||
mt_chan[chan] = 0;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Skip unit=%d\n", unit);
|
||
/* Allow time for tape to be restarted, before stop */
|
||
sim_activate(uptr, us_to_ticks(500));
|
||
return SCPE_OK;
|
||
|
||
case MT_RDS:
|
||
mode = 0100;
|
||
/* Fall through */
|
||
|
||
case MT_RDSB:
|
||
uptr->u5 &= ~MT_LWR; /* Not write command */
|
||
/* Post EOR */
|
||
if (uptr->u5 & MT_EOR) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post EOR\n", unit);
|
||
chan_set(chan, DEV_REOR);
|
||
uptr->u5 &= ~ MT_EOR;
|
||
sim_activate(uptr, T1_us);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* If tape mark pending, return it */
|
||
if (chan_test(chan, DEV_FULL) == 0 && uptr->u5 & MT_MARK) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post ", unit);
|
||
uptr->u5 &= ~(MT_CMDMSK|MT_MARK);
|
||
#ifdef I7010
|
||
if (astmode) {
|
||
ch = mode?017:054;
|
||
chan_write_char(chan, &ch, DEV_REOR);
|
||
if (mode) {
|
||
chan_clear(chan, STA_TWAIT);
|
||
sim_activate(uptr, us_to_ticks(100));
|
||
return SCPE_OK;
|
||
}
|
||
}
|
||
#endif
|
||
chan_set_attn(chan);
|
||
sim_activate(uptr, us_to_ticks(100));
|
||
return mt_error(uptr, chan, MTSE_TMK, dptr);
|
||
}
|
||
/* If at end of record, fill buffer */
|
||
if (uptr->u6 == (int32)uptr->hwmark) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit);
|
||
uptr->u3 += GAP_LEN;
|
||
if ((r = sim_tape_rdrecf(uptr, &mt_buffer[bufnum][0], &reclen,
|
||
BUFFSIZE)) != MTSE_OK) {
|
||
if (r == MTSE_TMK && uptr->u6 != -1) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "pend TM\n");
|
||
uptr->u5 |= MT_MARK;
|
||
r = MTSE_OK;
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "error=%d\n", r);
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
#ifdef I7010
|
||
/* Translate TM characters for 7010 */
|
||
if (r == MTSE_TMK && astmode) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read TM ");
|
||
ch = mode?017:054;
|
||
chan_write_char(chan, &ch, 0);
|
||
chan_set_attn(chan);
|
||
chan_set(chan, DEV_REOR);
|
||
chan_clear(chan, STA_TWAIT);
|
||
if (mode) {
|
||
sim_activate(uptr, T1_us);
|
||
return SCPE_OK;
|
||
}
|
||
chan_set_error(chan);
|
||
}
|
||
#else
|
||
chan_set(chan, DEV_REOR);
|
||
chan_set_attn(chan);
|
||
#endif
|
||
}
|
||
sim_activate(uptr, T1_us);
|
||
return mt_error(uptr, chan, r, dptr);
|
||
}
|
||
uptr->u6 = 0;
|
||
uptr->hwmark = reclen;
|
||
chan_clear(chan, CHS_EOF|CHS_ERR);
|
||
sim_debug(DEBUG_DETAIL, dptr, "%s Block %d chars\n",
|
||
(cmd == MT_RDS) ? "BCD" : "Binary", reclen);
|
||
#ifdef I7010
|
||
if (mode && mt_buffer[bufnum][0] == 017)
|
||
chan_set_eof(chan);
|
||
#endif
|
||
|
||
}
|
||
|
||
ch = mt_buffer[bufnum][uptr->u6++];
|
||
uptr->u3++;
|
||
/* Do BCD translation */
|
||
if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "%s parity error %d %03o\n",
|
||
(cmd == MT_RDS) ? "BCD" : "Binary", uptr->u6-1, ch);
|
||
#ifdef I7010
|
||
if (astmode)
|
||
ch = 054;
|
||
#endif
|
||
chan_set_error(chan);
|
||
|
||
}
|
||
#if I7090 | I704 | I701
|
||
/* Not needed on decimal machines */
|
||
if (mode) {
|
||
/* Map BCD to internal format */
|
||
ch ^= (ch & 020) << 1;
|
||
if (ch == 012)
|
||
ch = 0;
|
||
if (ch == 017) {
|
||
chan_set_error(chan); /* Force CRC error. */
|
||
if ((uptr->u5 & MT_RM) == 0) {
|
||
ch = 0;
|
||
uptr->u5 |= MT_RM;
|
||
mt_buffer[bufnum][uptr->u6] = 0;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
#ifdef I7010
|
||
if (mode) {
|
||
if (ch == 0120)
|
||
ch = 0;
|
||
}
|
||
#endif
|
||
ch &= 077;
|
||
|
||
/* Convert one word. */
|
||
switch (chan_write_char(chan, &ch, 0)) {
|
||
case END_RECORD:
|
||
sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n", unit);
|
||
/* If not read whole record, skip till end */
|
||
uptr->u5 |= MT_EOR;
|
||
if (uptr->u6 < (int32)uptr->hwmark) {
|
||
sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us);
|
||
uptr->u3 += (uptr->hwmark - uptr->u6);
|
||
uptr->u6 = uptr->hwmark; /* Force read next record */
|
||
}
|
||
sim_activate(uptr, T1_us);
|
||
break;
|
||
|
||
case DATA_OK:
|
||
sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n",
|
||
unit, uptr->u6, ch);
|
||
if (uptr->u6 >= (int32)uptr->hwmark) /* In IRG */
|
||
uptr->u5 |= MT_EOR;
|
||
sim_activate(uptr, T1_us);
|
||
break;
|
||
|
||
case TIME_ERROR:
|
||
sim_debug(DEBUG_DATA, dptr, "Read unit=%d timeout\n", unit);
|
||
uptr->u3 += (uptr->hwmark - uptr->u6);
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
uptr->u5 |= MT_SKIP;
|
||
sim_activate(uptr, ((uptr->hwmark - uptr->u6) * T1_us) + T2_us);
|
||
uptr->u6 = uptr->hwmark; /* Force read next record */
|
||
break;
|
||
}
|
||
return SCPE_OK;
|
||
|
||
|
||
/* Check mode */
|
||
case MT_WRS:
|
||
mode = 0100;
|
||
/* fall through */
|
||
case MT_WRSB:
|
||
uptr->u5 |= MT_LWR; /* write command */
|
||
if (uptr->u5 & MT_EGAP) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Write extended Gap unit=%d\n", unit);
|
||
uptr->u5 &= ~MT_EGAP;
|
||
r = sim_tape_wrgap(uptr, 35);
|
||
sim_activate(uptr, 10*T3_us);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
switch (chan_read_char(chan, &ch,
|
||
(uptr->u6 > BUFFSIZE) ? DEV_WEOR : 0)) {
|
||
case TIME_ERROR:
|
||
#if I7090 | I701 | I704
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
uptr->u5 |= MT_SKIP;
|
||
/* If no data was written, simulate a write gap */
|
||
if (uptr->u6 == 0) {
|
||
r = sim_tape_wrgap(uptr, 35);
|
||
if (r != MTSE_OK) {
|
||
mt_error(uptr, chan, r, dptr); /* Record errors */
|
||
return SCPE_OK;
|
||
}
|
||
}
|
||
#endif
|
||
/* fall through */
|
||
|
||
case END_RECORD:
|
||
if (uptr->u6 > 0) { /* Only if data in record */
|
||
reclen = uptr->hwmark;
|
||
sim_debug(DEBUG_DETAIL, dptr,
|
||
"Write unit=%d %s Block %d chars\n",
|
||
unit, (cmd == MT_WRS) ? "BCD" : "Binary", reclen);
|
||
r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen);
|
||
uptr->u3 += GAP_LEN;
|
||
uptr->u6 = 0;
|
||
uptr->hwmark = 0;
|
||
mt_error(uptr, chan, r, dptr); /* Record errors */
|
||
}
|
||
sim_activate(uptr, T2_us);
|
||
return SCPE_OK;
|
||
case DATA_OK:
|
||
/* Copy data to buffer */
|
||
ch &= 077;
|
||
#if I7090 | I701 | I704
|
||
/* Not needed on decimal machines */
|
||
if (mode) {
|
||
/* Do BCD translation */
|
||
ch ^= (ch & 020) << 1;
|
||
if (ch == 0)
|
||
ch = 012;
|
||
}
|
||
#endif
|
||
ch |= mode ^ parity_table[ch] ^ 0100;
|
||
mt_buffer[bufnum][uptr->u6++] = ch;
|
||
uptr->u3++;
|
||
sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %02o\n",
|
||
unit, uptr->u6, ch);
|
||
uptr->hwmark = uptr->u6;
|
||
break;
|
||
}
|
||
sim_activate(uptr, T1_us);
|
||
return SCPE_OK;
|
||
|
||
case MT_RDB:
|
||
uptr->u5 &= ~MT_LWR; /* not write command */
|
||
/* If tape mark pending, return it */
|
||
if (chan_test(chan, DEV_FULL) == 0 && uptr->u5 & MT_MARK) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post ", unit);
|
||
uptr->u5 &= ~(MT_CMDMSK|MT_MARK);
|
||
mt_chan[chan] &= MTC_BSY;
|
||
chan_clear(chan, DEV_SEL);
|
||
sim_activate(uptr, us_to_ticks(100));
|
||
return mt_error(uptr, chan, MTSE_TMK, dptr);
|
||
}
|
||
/* If at end of record, fill buffer */
|
||
if (uptr->u6 == (int32)uptr->hwmark) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit);
|
||
if ((r = sim_tape_rdrecr(uptr, &mt_buffer[bufnum][0], &reclen,
|
||
BUFFSIZE)) != MTSE_OK) {
|
||
uptr->u3 -= GAP_LEN;
|
||
sim_activate(uptr, T2_us);
|
||
if (r == MTSE_TMK && uptr->u6 != -1) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "pend TM\n");
|
||
uptr->u5 |= MT_MARK;
|
||
r = MTSE_OK;
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "error=%d\n", r);
|
||
uptr->u6 = uptr->hwmark;
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
chan_set_attn(chan);
|
||
chan_clear(chan, DEV_SEL);
|
||
mt_chan[chan] &= MTC_BSY;
|
||
}
|
||
return mt_error(uptr, chan, r, dptr);
|
||
}
|
||
uptr->u6 = 0;
|
||
uptr->hwmark = reclen;
|
||
chan_clear(chan, CHS_EOF|CHS_ERR);
|
||
sim_debug(DEBUG_DETAIL, dptr, "Binary Block %d chars\n", reclen);
|
||
}
|
||
|
||
ch = mt_buffer[bufnum][uptr->u6++];
|
||
uptr->u3--;
|
||
/* Do BCD translation */
|
||
if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) {
|
||
chan_set_error(chan);
|
||
chan_set_attn(chan);
|
||
}
|
||
ch &= 077;
|
||
|
||
/* Convert one word. */
|
||
switch (chan_write_char(chan, &ch,
|
||
(uptr->u6 >= (int32)uptr->hwmark) ? DEV_REOR : 0)) {
|
||
case END_RECORD:
|
||
sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n", unit);
|
||
if (uptr->u6 >= (int32)uptr->hwmark) {
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
uptr->u5 |= MT_SKIP;
|
||
uptr->u3 -= (uptr->hwmark-uptr->u6);
|
||
sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us);
|
||
chan_set(chan, DEV_REOR);
|
||
uptr->u6 = uptr->hwmark; /* Force read next record */
|
||
break;
|
||
}
|
||
/* fall through */
|
||
|
||
case DATA_OK:
|
||
sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n",
|
||
unit, uptr->u6, ch);
|
||
if (uptr->u6 >= (int32)uptr->hwmark) { /* In IRG */
|
||
uptr->u3 -= (uptr->hwmark-uptr->u6);
|
||
sim_activate(uptr, T2_us);
|
||
} else
|
||
sim_activate(uptr, T1_us);
|
||
break;
|
||
|
||
case TIME_ERROR:
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
uptr->u5 |= MT_SKIP;
|
||
uptr->u3 -= (uptr->hwmark-uptr->u6);
|
||
sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us);
|
||
uptr->u6 = uptr->hwmark; /* Force read next record */
|
||
break;
|
||
}
|
||
return SCPE_OK;
|
||
|
||
case MT_WEF:
|
||
uptr->u5 &= ~MT_LWR; /* not write command */
|
||
if (uptr->u5 & MT_EGAP) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Write extended Gap unit=%d\n", unit);
|
||
uptr->u5 &= ~MT_EGAP;
|
||
r = sim_tape_wrgap(uptr, 35);
|
||
sim_activate(uptr, 10*T3_us);
|
||
return SCPE_OK;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit);
|
||
uptr->u5 &= ~(MT_CMDMSK|MT_MARK);
|
||
uptr->u5 |= (MT_RDY);
|
||
r = sim_tape_wrtmk(uptr);
|
||
uptr->u3 += GAP_LEN;
|
||
mt_chan[chan] &= ~MTC_BSY;
|
||
sim_activate(uptr, T2_us);
|
||
#if I7010 | I7080
|
||
chan_set(chan, DEV_REOR);
|
||
#endif
|
||
break;
|
||
|
||
case MT_BSR:
|
||
sim_debug(DEBUG_DETAIL, dptr, "Backspace rec unit=%d ", unit);
|
||
/* If last command was a write, put extended gap on tape */
|
||
if (uptr->u5 & MT_LWR) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Write extended Gap unit=%d bsr\n", unit);
|
||
r = sim_tape_wrgap(uptr, 35);
|
||
uptr->u5 &= ~MT_LWR;
|
||
sim_activate(uptr, 10*T3_us);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Clear tape mark, command, idle since we will need to change dir */
|
||
uptr->u5 &= ~(MT_CMDMSK | MT_EOT | MT_RDY | MT_LWR);
|
||
r = sim_tape_sprecr(uptr, &reclen);
|
||
if (r != MTSE_BOT)
|
||
uptr->u3 -= GAP_LEN;
|
||
mt_chan[chan] &= ~MTC_BSY;
|
||
if (r == MTSE_TMK) {
|
||
#ifdef I7080
|
||
chan_set_eof(chan);
|
||
#endif
|
||
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
|
||
sim_activate(uptr, T2_us);
|
||
return SCPE_OK;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d \n", reclen);
|
||
uptr->u3 -= reclen;
|
||
sim_activate(uptr, T2_us + (reclen * T1_us));
|
||
return SCPE_OK;
|
||
|
||
case MT_BSF:
|
||
uptr->u5 &= ~(MT_IDLE | MT_RDY | MT_EOT | MT_LWR);
|
||
r = sim_tape_sprecr(uptr, &reclen);
|
||
if (r != MTSE_BOT)
|
||
uptr->u3 -= GAP_LEN;
|
||
/* If we hit mark or end of tape */
|
||
if (r == MTSE_TMK || r == MTSE_BOT) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Backspace file unit=%d\n", unit);
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
mt_chan[chan] &= ~MTC_BSY;
|
||
sim_activate(uptr, T2_us);
|
||
} else {
|
||
uptr->u3 -= reclen;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Backspace file record unit=%d\n", unit);
|
||
sim_activate(uptr, T2_us + (reclen * T1_us));
|
||
}
|
||
return SCPE_OK;
|
||
|
||
case MT_SKR:
|
||
sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d ", unit);
|
||
if (uptr->u5 & MT_CLRIND) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "clear ind\n");
|
||
#if I7010
|
||
if ((uptr->u5 & MT_MARK) == 0) {
|
||
chan_clear(chan, STA_PEND);
|
||
}
|
||
#endif
|
||
chan_clear(chan, STA_TWAIT);
|
||
uptr->u5 &= ~(MT_CMDMSK | MT_EOT | MT_LWR | MT_MARK | MT_CLRIND);
|
||
uptr->u5 |= (MT_RDY | MT_IDLE);
|
||
mt_chan[chan] &= ~MTC_BSY;
|
||
sim_activate(uptr, (uptr->u6 * T1_us) + T2_us);
|
||
return SCPE_OK;
|
||
}
|
||
uptr->u5 |= (MT_CLRIND);
|
||
r = sim_tape_sprecf(uptr, &reclen);
|
||
uptr->u3 += GAP_LEN;
|
||
uptr->u6 = reclen;
|
||
#if I7080
|
||
chan_clear(chan, STA_TWAIT);
|
||
#endif
|
||
#ifdef I7010
|
||
chan_set(chan, STA_PEND);
|
||
#endif
|
||
/* We are like read that transfers nothing */
|
||
chan_set(chan, DEV_REOR);
|
||
/* We don't set EOF on SKR */
|
||
if (r == MTSE_TMK) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
|
||
uptr->u5 |= MT_MARK;
|
||
uptr->u6 = 1;
|
||
sim_activate(uptr, 2*T1_us);
|
||
return SCPE_OK;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen);
|
||
sim_activate(uptr, 2*T1_us);
|
||
break;
|
||
|
||
case MT_ERG:
|
||
sim_debug(DEBUG_DETAIL, dptr, "Erase unit=%d\n", unit);
|
||
uptr->u5 &= ~(MT_CMDMSK|MT_MARK|MT_LWR);
|
||
uptr->u5 |= (MT_RDY | MT_IDLE);
|
||
#if I7010 | I7080
|
||
chan_clear(chan, STA_TWAIT);
|
||
#endif
|
||
r = sim_tape_wrgap(uptr, 35);
|
||
uptr->u3 += GAP_LEN;
|
||
mt_chan[chan] &= ~MTC_BSY;
|
||
sim_activate(uptr, 10*T3_us);
|
||
break;
|
||
|
||
case MT_REW:
|
||
sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d %d %d\n", unit, uptr->u3,
|
||
uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200);
|
||
uptr->u5 &= ~(MT_CMDMSK | MT_IDLE | MT_RDY | MT_LWR);
|
||
if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) {
|
||
uptr->u5 |= MT_HREW;
|
||
sim_activate(uptr, us_to_ticks(5000000));
|
||
} else {
|
||
uptr->u5 |= MT_LREW;
|
||
sim_activate(uptr, 300);
|
||
}
|
||
mt_chan[chan] &= ~MTC_BSY;
|
||
break;
|
||
|
||
case MT_RUN:
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unload unit=%d\n", unit);
|
||
uptr->u5 &= ~(MT_CMDMSK | MT_IDLE | MT_RDY | MT_LWR);
|
||
uptr->u5 |= MT_UNLOAD;
|
||
if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) {
|
||
uptr->u5 |= MT_HREW;
|
||
sim_activate(uptr, us_to_ticks(5000000));
|
||
} else {
|
||
uptr->u5 |= MT_LREW;
|
||
sim_activate(uptr, 300);
|
||
}
|
||
mt_chan[chan] &= ~MTC_BSY;
|
||
return SCPE_OK;
|
||
|
||
case MT_HREW:
|
||
sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d HS\n", unit);
|
||
if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) {
|
||
uptr->u3 -= (uptr->flags & MTUF_LDN) ? 1666 :4625;
|
||
sim_activate(uptr, us_to_ticks(16000));
|
||
} else {
|
||
uptr->u5 &= ~(MT_CMDMSK);
|
||
uptr->u5 |= MT_LREW;
|
||
sim_activate(uptr, us_to_ticks(5000000));
|
||
}
|
||
return SCPE_OK;
|
||
|
||
case MT_LREW:
|
||
sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d LS\n", unit);
|
||
if (uptr->u3 > 0) {
|
||
uptr->u3 -= (uptr->flags & MTUF_LDN) ? 373 :1036;
|
||
sim_activate(uptr, us_to_ticks(16000));
|
||
return SCPE_OK;
|
||
} else {
|
||
if(uptr->u5 & MT_UNLOAD)
|
||
r = sim_tape_detach(uptr);
|
||
else
|
||
r = sim_tape_rewind(uptr);
|
||
uptr->u5 &= ~(MT_CMDMSK|MT_UNLOAD);
|
||
uptr->u5 |= MT_RDY;
|
||
uptr->u3 = 0;
|
||
}
|
||
break;
|
||
}
|
||
return mt_error(uptr, chan, r, dptr);
|
||
}
|
||
|
||
/* Boot from given device */
|
||
t_stat
|
||
mt_boot(int32 unit_num, DEVICE * dptr)
|
||
{
|
||
UNIT *uptr = &dptr->units[unit_num];
|
||
uint16 dev = unit_num + 020 + mt_dib.addr;
|
||
#if I7090 | I704 | I701
|
||
t_mtrlnt reclen;
|
||
t_stat r;
|
||
#endif
|
||
|
||
if ((uptr->flags & UNIT_ATT) == 0)
|
||
return SCPE_UNATT; /* attached? */
|
||
|
||
/* Start a read. */
|
||
if (mt_cmd(dptr->units, IO_RDS, dev) != SCPE_OK)
|
||
return STOP_IONRDY;
|
||
|
||
#if I7090 | I704 | I701
|
||
r = sim_tape_rdrecf(uptr, &mt_buffer[GET_DEV_BUF(dptr->flags)][0], &reclen,
|
||
BUFFSIZE);
|
||
if (r != SCPE_OK)
|
||
return r;
|
||
uptr->u6 = 0;
|
||
uptr->hwmark = reclen;
|
||
|
||
/* Copy first three records. */
|
||
mt_read_buff(uptr, MT_RDSB, dptr, &M[0]);
|
||
mt_read_buff(uptr, MT_RDSB, dptr, &M[1]);
|
||
if (UNIT_G_CHAN(uptr->flags) != 0)
|
||
mt_read_buff(uptr, MT_RDSB, dptr, &M[2]);
|
||
/* Make sure channel is set to start reading rest. */
|
||
#endif
|
||
return chan_boot(unit_num, dptr);
|
||
}
|
||
|
||
void
|
||
mt_ini(UNIT * uptr, t_bool f)
|
||
{
|
||
int chan = UNIT_G_CHAN(uptr->flags);
|
||
|
||
if (uptr->flags & UNIT_ATT)
|
||
uptr->u5 = MT_RDY;
|
||
else
|
||
uptr->u5 = 0;
|
||
uptr->u3 = 0;
|
||
mt_chan[chan] = 0;
|
||
}
|
||
|
||
t_stat
|
||
mt_reset(DEVICE * dptr)
|
||
{
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
mt_tape_density(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
return sim_tape_set_dens(uptr, (val == MTUF_LDN) ? MT_DENS_200 : MT_DENS_556, NULL, NULL);
|
||
}
|
||
|
||
t_stat
|
||
mt_attach(UNIT * uptr, CONST char *file)
|
||
{
|
||
t_stat r;
|
||
|
||
if ((r = sim_tape_attach_ex(uptr, file, 0, 0)) != SCPE_OK)
|
||
return r;
|
||
sim_tape_set_dens(uptr, (uptr->flags == MTUF_LDN) ? MT_DENS_200 : MT_DENS_556, NULL, NULL);
|
||
uptr->u3 = 0;
|
||
uptr->u5 |= MT_RDY;
|
||
uptr->flags |= MTUF_ONLINE;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
mt_detach(UNIT * uptr)
|
||
{
|
||
uptr->u3 = 0;
|
||
uptr->u5 = 0;
|
||
uptr->flags &= ~MTUF_ONLINE;
|
||
return sim_tape_detach(uptr);
|
||
}
|
||
|
||
t_stat
|
||
mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
||
{
|
||
fprintf (st, "%s\n\n", mt_description(dptr));
|
||
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\n");
|
||
fprintf (st, " sim> SET %s LENGTH 15\n\n", dptr->name);
|
||
fprintf (st, "The mag tape drives support the BOOT command\n\n");
|
||
help_set_chan_type(st, dptr, "Mag tape");
|
||
sim_tape_attach_help (st, dptr, uptr, flag, cptr);
|
||
fprint_set_help(st, dptr);
|
||
fprint_show_help(st, dptr);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *
|
||
mt_description(DEVICE *dptr)
|
||
{
|
||
return "IBM 729 Magnetic tape unit";
|
||
}
|
||
|
||
#endif
|