mirror of
https://github.com/rcornwell/sims.git
synced 2026-01-31 13:52:50 +00:00
1152 lines
44 KiB
C
1152 lines
44 KiB
C
/* ibm360_mt.c: IBM 360 2400 Magnetic tape controller
|
||
|
||
Copyright (c) 2017-2020, 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 "ibm360_defs.h"
|
||
#include "sim_tape.h"
|
||
|
||
#ifdef NUM_DEVS_MT
|
||
#define BUFFSIZE (64 * 1024)
|
||
#define MTUF_9TR (1 << MTUF_V_UF)
|
||
#define DEV_BUF_NUM(x) (((x) & 07) << DEV_V_UF)
|
||
#define GET_DEV_BUF(x) (((x) >> DEV_V_UF) & 07)
|
||
#define MT_BUSY (1 << (MTUF_V_UF + 1)) /* Flag to send a CUE */
|
||
#define MTUF_3400 (1 << (MTUF_V_UF + 2))
|
||
#define UNIT_MT(x) UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | MTUF_9TR | \
|
||
DEV_BUF_NUM(x)
|
||
|
||
|
||
#define MT_WRITE 0x01 /* Write command */
|
||
#define MT_READ 0x02 /* Read command */
|
||
#define MT_RDBK 0x0c /* Read Backward */
|
||
#define MT_SENSE 0x04 /* Sense command */
|
||
#define MT_REW 0x07 /* Rewind command */
|
||
#define MT_RUN 0x0f /* Rewind and unload */
|
||
#define MT_ERG 0x17 /* Erase Gap */
|
||
#define MT_WTM 0x1f /* Write Tape Mark */
|
||
#define MT_BSR 0x27 /* Back space record */
|
||
#define MT_BSF 0x2f /* Back space file */
|
||
#define MT_FSR 0x37 /* Forward space record */
|
||
#define MT_FSF 0x3f /* Forward space file */
|
||
#define MT_SECURE_ERA 0x97 /* 3400 Security erase */
|
||
#define MT_SENSE_RES 0xf4 /* 3400 Sense reserve */
|
||
#define MT_SENSE_REL 0xb4 /* 3400 Sense release */
|
||
#define MT_RTIE 0x1b /* Request track in error */
|
||
#define MT_MODE 0x03 /* Mode command */
|
||
#define MT_MODEMSK 0x07 /* Mode Mask */
|
||
|
||
#define MT_MDEN_200 0x000 /* 200 BPI mode 7 track only */
|
||
#define MT_MDEN_556 0x040 /* 556 BPI mode 7 track only */
|
||
#define MT_MDEN_800 0x080 /* 800 BPI mode 7 track only */
|
||
#define MT_MDEN_1600 0x0c0 /* 1600 BPI mode 9 track only */
|
||
#define MT_MDEN_6520 0x1c0 /* 6520 BPI mode 9 track only */
|
||
#define MT_MDEN_MSK 0x1c0 /* Density mask */
|
||
|
||
#define MT_CTL_MSK 0x38 /* Mask for control flags */
|
||
#define MT_CTL_NOP 0x00 /* Nop control mode */
|
||
#define MT_CTL_NRZI 0x08 /* 9 track 800 bpi mode */
|
||
#define MT_CTL_RST 0x10 /* Set density, odd, convert on, trans off */
|
||
#define MT_CTL_NOP2 0x18 /* 9 track 1600 NRZI mode */
|
||
#define MT_CTL_MD0 0x20 /* Set density, even, convert off, trans off */
|
||
#define MT_CTL_MD1 0x28 /* Set density, even, convert off, trans on */
|
||
#define MT_CTL_MD2 0x30 /* Set density, odd, convert off, trans off */
|
||
#define MT_CTL_MD3 0x38 /* Set density, odd, convert off, trans on */
|
||
|
||
/* in u3 is device command code and status */
|
||
#define MT_CMDMSK 0x0003f /* Command being run */
|
||
#define MT_READDONE 0x00400 /* Read finished, end channel */
|
||
#define MT_MARK 0x00800 /* Sensed tape mark in move command */
|
||
#define MT_ODD 0x01000 /* Odd parity */
|
||
#define MT_TRANS 0x02000 /* Translation turned on ignored 9 track */
|
||
#define MT_CONV 0x04000 /* Data converter on ignored 9 track */
|
||
#define MT_CMDREW 0x10000 /* Rewind being done */
|
||
#define MT_CMDRUN 0x20000 /* Unload being done */
|
||
#define MT_CHAIN 0x40000 /* Start of command chain */
|
||
|
||
/* Upper 11 bits of u3 hold the device address */
|
||
|
||
/* in u4 is current buffer position */
|
||
|
||
/* in u5 packs sense byte 0,1 and 3 */
|
||
/* Sense byte 0 */
|
||
#define SNS_CMDREJ 0x80 /* Command reject */
|
||
#define SNS_INTVENT 0x40 /* Unit intervention required */
|
||
#define SNS_BUSCHK 0x20 /* Parity error on bus */
|
||
#define SNS_EQUCHK 0x10 /* Equipment check */
|
||
#define SNS_DATCHK 0x08 /* Data Check */
|
||
#define SNS_OVRRUN 0x04 /* Data overrun */
|
||
#define SNS_WCZERO 0x02 /* Write with no data */
|
||
#define SNS_CVTCHK 0x01 /* Data conversion error */
|
||
|
||
/* Sense byte 1 */
|
||
#define SNS_NOISE 0x80 /* Noise record */
|
||
#define SNS_TUASTA 0x40 /* Selected and ready */
|
||
#define SNS_TUBSTA 0x20 /* Not ready, rewinding. */
|
||
#define SNS_7TRACK 0x10 /* Seven track unit */
|
||
#define SNS_LOAD 0x08 /* Load Point */
|
||
#define SNS_WR 0x04 /* Unit write */
|
||
#define SNS_WRP 0x02 /* No write ring */
|
||
#define SNS_DENS 0x01 /* Density error 9tr only */
|
||
|
||
/* Sense byte 2 */
|
||
#define SNS_BYTE2 0x03 /* Not supported feature */
|
||
|
||
/* Sense byte 3 */
|
||
#define SNS_VRC 0x80 /* Veritical parity error */
|
||
#define SNS_LRCR 0x40 /* Logituntial parity error */
|
||
#define SNS_SKEW 0x20 /* Skew */
|
||
#define SNS_CRC 0x10 /* CRC error. 9t only */
|
||
#define SNS_SKEWVRC 0x08 /* VRC Skew */
|
||
#define SNS_PE 0x04 /* Phase encoding */
|
||
#define SNS_BACK 0x01 /* tape in backward status */
|
||
|
||
#define SNS_BYTE4 0x00 /* Hardware errors not supported */
|
||
#define SNS_BYTE5 0x00 /* Hardware errors not supported */
|
||
|
||
#define MT_CONV1 0x40
|
||
#define MT_CONV2 0x80
|
||
#define MT_CONV3 0xc0
|
||
|
||
/* u6 holds the packed characters and unpack counter */
|
||
#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF)
|
||
#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF
|
||
|
||
#define CMD u3
|
||
#define POS u4
|
||
#define SNS u5
|
||
#define CPOS u6
|
||
|
||
uint8 mt_startio(UNIT *uptr);
|
||
uint8 mt_startcmd(UNIT *uptr, uint8 cmd);
|
||
t_stat mt_srv(UNIT *);
|
||
t_stat mt_boot(int32, DEVICE *);
|
||
void mt_ini(UNIT *, t_bool);
|
||
t_stat mt_boot(int32, DEVICE *);
|
||
t_stat mt_attach(UNIT *, CONST char *);
|
||
t_stat mt_detach(UNIT *);
|
||
t_stat mt_help (FILE *, DEVICE *, UNIT *, int32, const char *);
|
||
const char *mt_description (DEVICE *);
|
||
|
||
|
||
/* One buffer per channel */
|
||
uint8 mt_buffer[NUM_DEVS_MT][BUFFSIZE];
|
||
uint8 mt_busy[NUM_DEVS_MT];
|
||
|
||
MTAB mt_mod[] = {
|
||
{MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL},
|
||
{MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL},
|
||
{MTUF_9TR, 0, "7 track", "7T", NULL},
|
||
{MTUF_9TR, MTUF_9TR, "9 track", "9T", NULL},
|
||
{MTUF_3400, 0, "2400", "2400", NULL},
|
||
{MTUF_3400, MTUF_3400, "3400", "3400", NULL},
|
||
{MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT",
|
||
&sim_tape_set_fmt, &sim_tape_show_fmt, NULL},
|
||
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr,
|
||
&show_dev_addr, NULL},
|
||
{0}
|
||
};
|
||
|
||
UNIT mta_unit[] = {
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0, UNIT_ADDR(0x180)}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0, UNIT_ADDR(0x181)}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0, UNIT_ADDR(0x182)}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0, UNIT_ADDR(0x183)}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0, UNIT_ADDR(0x184)}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0, UNIT_ADDR(0x185)}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0, UNIT_ADDR(0x186)}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(0), 0), 0, UNIT_ADDR(0x187)}, /* 7 */
|
||
};
|
||
|
||
struct dib mta_dib = { 0xF8, NUM_UNITS_MT, mt_startio, mt_startcmd, NULL, mta_unit, mt_ini};
|
||
|
||
DEVICE mta_dev = {
|
||
"MTA", mta_unit, NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, NULL, &mt_boot, &mt_attach, &mt_detach,
|
||
&mta_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
|
||
UNIT mtb_unit[] = {
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0, UNIT_ADDR(0x280)}, /* 0 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0, UNIT_ADDR(0x281)}, /* 1 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0, UNIT_ADDR(0x282)}, /* 2 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0, UNIT_ADDR(0x283)}, /* 3 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0, UNIT_ADDR(0x284)}, /* 4 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0, UNIT_ADDR(0x285)}, /* 5 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0, UNIT_ADDR(0x286)}, /* 6 */
|
||
{UDATA(&mt_srv, UNIT_MT(1), 0), 0, UNIT_ADDR(0x287)}, /* 7 */
|
||
};
|
||
|
||
struct dib mtb_dib = { 0xF8, NUM_UNITS_MT, mt_startio, mt_startcmd, NULL, mtb_unit, mt_ini};
|
||
|
||
DEVICE mtb_dev = {
|
||
"MTB", mtb_unit, NULL, mt_mod,
|
||
NUM_UNITS_MT, 8, 15, 1, 8, 8,
|
||
NULL, NULL, NULL, &mt_boot, &mt_attach, &mt_detach,
|
||
&mtb_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_TAPE, 0,
|
||
dev_debug, NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
||
};
|
||
#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
|
||
};
|
||
|
||
uint8 bcd_to_ebcdic[64] = {
|
||
0x40, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
|
||
0xf8, 0xf9, 0xf0, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
||
0x7a, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
||
0xe8, 0xe9, 0xe0, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||
0x60, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
|
||
0xd8, 0xd9, 0xd0, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||
0x50, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
||
0xc8, 0xc9, 0xc0, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f};
|
||
|
||
uint8 mt_startio(UNIT *uptr) {
|
||
DEVICE *dptr = find_dev_from_unit(uptr);
|
||
unsigned int i;
|
||
|
||
if (mt_busy[GET_DEV_BUF(dptr->flags)] != 0) {
|
||
sim_debug(DEBUG_CMD, dptr, "busy\n");
|
||
return SNS_BSY;
|
||
}
|
||
if ((uptr->CMD & (MT_CMDREW|MT_CMDRUN)) != 0) {
|
||
sim_debug(DEBUG_CMD, dptr, "rew/run\n");
|
||
return SNS_BSY;
|
||
}
|
||
|
||
/* Check if controller is free */
|
||
for (i = 0; i < dptr->numunits; i++) {
|
||
if ((dptr->units[i].CMD & MT_CMDMSK) != 0) {
|
||
uptr->flags |= MT_BUSY; /* Flag we need to send CUE */
|
||
return SNS_SMS|SNS_BSY;
|
||
}
|
||
}
|
||
uptr->CMD &= ~MT_CHAIN; /* Clear start of chain flag */
|
||
sim_debug(DEBUG_CMD, dptr, "start io\n");
|
||
return 0;
|
||
}
|
||
|
||
uint8 mt_startcmd(UNIT *uptr, uint8 cmd) {
|
||
DEVICE *dptr = find_dev_from_unit(uptr);
|
||
int unit = (uptr - dptr->units);
|
||
uint8 f = 0;
|
||
|
||
if (mt_busy[GET_DEV_BUF(dptr->flags)] != 0 || (uptr->CMD & MT_CMDMSK) != 0) {
|
||
sim_debug(DEBUG_CMD, dptr, "CMD busy unit=%d %x\n", unit, cmd);
|
||
uptr->flags |= MT_BUSY;
|
||
return SNS_BSY;
|
||
}
|
||
|
||
if (uptr->flags & MT_BUSY)
|
||
f = SNS_CTLEND;
|
||
|
||
sim_debug(DEBUG_CMD, dptr, "CMD unit=%d %x\n", unit, cmd);
|
||
|
||
switch (cmd & 0xF) {
|
||
case 0x7: /* Tape motion */
|
||
case 0xf: /* Tape motion */
|
||
case 0x1: /* Write command */
|
||
case 0x2: /* Read command */
|
||
case 0xc: /* Read backward */
|
||
uptr->SNS = 0;
|
||
goto do_cmd;
|
||
|
||
case 0x4: /* Sense */
|
||
if ((cmd & 0xf0) != 0) {
|
||
uptr->SNS |= SNS_CMDREJ;
|
||
uptr->flags &= ~MT_BUSY;
|
||
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK|f;
|
||
}
|
||
do_cmd:
|
||
if ((uptr->CMD & MT_CMDREW) != 0) {
|
||
sim_debug(DEBUG_CMD, dptr, "CMD rewinding unit=%d %x\n", unit, cmd);
|
||
return SNS_BSY;
|
||
}
|
||
if ((uptr->CMD & MT_CMDRUN) != 0) {
|
||
sim_debug(DEBUG_CMD, dptr, "CMD unloading unit=%d %x\n", unit, cmd);
|
||
uptr->SNS |= SNS_INTVENT;
|
||
uptr->flags &= ~MT_BUSY;
|
||
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK|f;
|
||
}
|
||
if ((uptr->flags & UNIT_ATT) == 0) {
|
||
uptr->SNS |= SNS_INTVENT;
|
||
uptr->flags &= ~MT_BUSY;
|
||
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK|f;
|
||
}
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->CMD |= cmd & MT_CMDMSK;
|
||
sim_activate(uptr, 1000); /* Start unit off */
|
||
CLR_BUF(uptr);
|
||
uptr->POS = 0;
|
||
uptr->CPOS = 0;
|
||
mt_busy[GET_DEV_BUF(dptr->flags)] = 1;
|
||
if ((cmd & 0x7) == 0x7) { /* Quick end channel on control */
|
||
uptr->flags &= ~MT_BUSY;
|
||
return SNS_CHNEND|f;
|
||
}
|
||
return 0;
|
||
|
||
case 0x3: /* Control */
|
||
case 0xb: /* Control */
|
||
uptr->SNS = 0;
|
||
if ((uptr->flags & UNIT_ATT) == 0) {
|
||
uptr->SNS |= SNS_INTVENT;
|
||
uptr->flags &= ~MT_BUSY;
|
||
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK|f;
|
||
}
|
||
if ((uptr->flags & MTUF_9TR) == 0) {
|
||
uptr->SNS |= (SNS_7TRACK << 8);
|
||
uptr->CMD |= MT_ODD;
|
||
if ((cmd & 0xc0) == 0xc0) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
return SNS_CHNEND|SNS_DEVEND|f;
|
||
}
|
||
switch((cmd >> 3) & 07) {
|
||
case 0: /* NOP */
|
||
case 1: /* Diagnostics */
|
||
case 3:
|
||
uptr->flags &= ~MT_BUSY;
|
||
return SNS_CHNEND|SNS_DEVEND|f;
|
||
case 2: /* Reset condition */
|
||
uptr->CMD &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
|
||
uptr->CMD |= (cmd & MT_MDEN_MSK) | MT_ODD | MT_CONV;
|
||
break;
|
||
case 4:
|
||
uptr->CMD &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
|
||
uptr->CMD |= (cmd & MT_MDEN_MSK);
|
||
break;
|
||
case 5:
|
||
uptr->CMD &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
|
||
uptr->CMD |= (cmd & MT_MDEN_MSK) | MT_TRANS;
|
||
break;
|
||
case 6:
|
||
uptr->CMD &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
|
||
uptr->CMD |= (cmd & MT_MDEN_MSK) | MT_ODD;
|
||
break;
|
||
case 7:
|
||
uptr->CMD &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
|
||
uptr->CMD |= (cmd & MT_MDEN_MSK) | MT_ODD | MT_TRANS;
|
||
break;
|
||
}
|
||
} else {
|
||
uptr->CMD &= ~MT_MDEN_MSK;
|
||
if (cmd & 0x8)
|
||
uptr->CMD |= MT_MDEN_800;
|
||
else if (cmd & 0x10)
|
||
uptr->CMD |= MT_MDEN_6520;
|
||
else
|
||
uptr->CMD |= MT_MDEN_1600;
|
||
}
|
||
uptr->SNS = 0;
|
||
break;
|
||
|
||
case 0x0: /* Status */
|
||
break;
|
||
|
||
default: /* invalid command */
|
||
uptr->SNS |= SNS_CMDREJ;
|
||
break;
|
||
}
|
||
uptr->flags &= ~MT_BUSY;
|
||
if (uptr->SNS & 0xff)
|
||
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK|f;
|
||
return SNS_CHNEND|SNS_DEVEND|f;
|
||
}
|
||
|
||
/* Map simH errors into machine errors */
|
||
t_stat mt_error(UNIT * uptr, uint16 addr, t_stat r, DEVICE * dptr)
|
||
{
|
||
uint8 flags = SNS_CHNEND|SNS_DEVEND;
|
||
|
||
if (uptr->flags & MT_BUSY) {
|
||
flags |= SNS_CTLEND;
|
||
uptr->flags &= ~MT_BUSY;
|
||
}
|
||
|
||
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
|
||
switch (r) {
|
||
case MTSE_OK: /* no error */
|
||
break;
|
||
|
||
case MTSE_TMK: /* tape mark */
|
||
sim_debug(DEBUG_EXP, dptr, "MARK ");
|
||
chan_end(addr, flags|SNS_UNITEXP);
|
||
return SCPE_OK;
|
||
|
||
case MTSE_WRP: /* write protected */
|
||
case MTSE_UNATT: /* unattached */
|
||
sim_debug(DEBUG_EXP, dptr, "ATTENTION %d ", r);
|
||
break;
|
||
|
||
case MTSE_IOERR: /* IO error */
|
||
case MTSE_FMT: /* invalid format */
|
||
case MTSE_RECE: /* error in record */
|
||
sim_debug(DEBUG_EXP, dptr, "ERROR %d ", r);
|
||
break;
|
||
case MTSE_BOT: /* beginning of tape */
|
||
sim_debug(DEBUG_EXP, dptr, "BOT ");
|
||
break;
|
||
case MTSE_INVRL: /* invalid rec lnt */
|
||
break;
|
||
case MTSE_EOM: /* end of medium */
|
||
sim_debug(DEBUG_EXP, dptr, "EOT ");
|
||
chan_end(addr, flags|SNS_UNITEXP);
|
||
return SCPE_OK;
|
||
}
|
||
chan_end(addr, flags);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Handle processing of tape requests. */
|
||
t_stat mt_srv(UNIT * uptr)
|
||
{
|
||
uint16 addr = GET_UADDR(uptr->CMD);
|
||
DEVICE *dptr = find_dev_from_unit(uptr);
|
||
int unit = (uptr - dptr->units);
|
||
int cmd = uptr->CMD & 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;
|
||
|
||
if ((uptr->flags & UNIT_ATT) == 0) {
|
||
uptr->SNS |= SNS_INTVENT;
|
||
if (cmd != MT_SENSE) {
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
}
|
||
|
||
if ((uptr->CMD & MT_CMDREW) != 0) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d\n", unit);
|
||
uptr->CMD &= ~(MT_CMDREW);
|
||
r = sim_tape_rewind(uptr);
|
||
set_devattn(addr, SNS_DEVEND);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
if ((uptr->CMD & MT_CMDRUN) != 0) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Unload unit=%d\n", unit);
|
||
uptr->CMD &= ~(MT_CMDRUN);
|
||
return sim_tape_detach(uptr);
|
||
}
|
||
|
||
switch (cmd & 0xf) {
|
||
case 0: /* No command, stop tape */
|
||
sim_debug(DEBUG_DETAIL, dptr, "Idle unit=%d\n", unit);
|
||
break;
|
||
|
||
case MT_SENSE:
|
||
ch = uptr->SNS & 0xff;
|
||
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 1 %x\n", unit, ch);
|
||
chan_write_byte(addr, &ch) ;
|
||
ch = (uptr->SNS >> 8) & 0xff;
|
||
if ((uptr->flags & MTUF_9TR) == 0)
|
||
ch |= SNS_7TRACK;
|
||
if ((uptr->flags & UNIT_ATT) != 0) {
|
||
if (sim_tape_wrp(uptr))
|
||
ch |= SNS_WRP;
|
||
if (sim_tape_bot(uptr))
|
||
ch |= SNS_LOAD;
|
||
ch |= SNS_TUASTA;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 2 %x\n", unit, ch);
|
||
chan_write_byte(addr, &ch) ;
|
||
ch = SNS_BYTE2;
|
||
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 3 %x\n", unit, ch);
|
||
chan_write_byte(addr, &ch) ;
|
||
ch = (uptr->SNS >> 16) & 0xff;
|
||
if ((uptr->flags & MTUF_9TR) != 0)
|
||
ch |= 04;
|
||
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 4 %x\n", unit, ch);
|
||
chan_write_byte(addr, &ch) ;
|
||
ch = SNS_BYTE4;
|
||
chan_write_byte(addr, &ch) ;
|
||
ch = SNS_BYTE5;
|
||
chan_write_byte(addr, &ch);
|
||
if ((uptr->flags & MTUF_3400) != 0) {
|
||
/* Sense byte 6 */
|
||
ch = 0x23; /* Model 3, Support dual density */
|
||
if ((uptr->flags & MTUF_9TR) == 0) {
|
||
ch |= 0x80; /* Indicate 7 track */
|
||
}
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 7 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 8 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 9 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 10 */
|
||
ch = (uptr->CMD & SNS_CMDREJ);
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 11 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 12 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 13 */
|
||
ch = (uptr->flags & MTUF_9TR) ? 0x80 : 0x40;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 14 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 15 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 16 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 17 */
|
||
ch = 0; /* No features */
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 18 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 19 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 20 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 21 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 22 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
/* Sense byte 23 */
|
||
ch = 0;
|
||
chan_write_byte(addr, &ch);
|
||
}
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
|
||
}
|
||
break;
|
||
|
||
case MT_READ:
|
||
|
||
if (uptr->CMD & MT_READDONE) {
|
||
uptr->CMD &= ~(MT_CMDMSK|MT_READDONE);
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
|
||
}
|
||
break;
|
||
}
|
||
|
||
/* If empty buffer, fill */
|
||
if (BUF_EMPTY(uptr)) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit);
|
||
if ((r = sim_tape_rdrecf(uptr, &mt_buffer[bufnum][0], &reclen,
|
||
BUFFSIZE)) != MTSE_OK) {
|
||
sim_debug(DEBUG_DETAIL, dptr, " error %d\n", r);
|
||
uptr->CMD &= ~(MT_CMDMSK|MT_READDONE);
|
||
return mt_error(uptr, addr, r, dptr);
|
||
}
|
||
uptr->POS = 0;
|
||
uptr->CPOS = 0;
|
||
uptr->hwmark = reclen;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Block %d chars\n", reclen);
|
||
}
|
||
|
||
ch = mt_buffer[bufnum][uptr->POS++];
|
||
/* if we are a 7track tape, handle conversion */
|
||
if ((uptr->flags & MTUF_9TR) == 0) {
|
||
mode = (uptr->CMD & MT_ODD) ? 0 : 0100;
|
||
if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Parity error unit=%d %d %03o\n",
|
||
unit, uptr->POS-1, ch);
|
||
uptr->SNS |= (SNS_VRC << 16) | SNS_DATCHK;
|
||
}
|
||
ch &= 077;
|
||
if (uptr->CMD & MT_TRANS)
|
||
ch = bcd_to_ebcdic[ch];
|
||
if (uptr->CMD & MT_CONV) {
|
||
sim_debug(DEBUG_DATA, dptr, "Read raw data unit=%d %d %02x %02x\n",
|
||
unit, uptr->POS, ch, uptr->CPOS);
|
||
if (uptr->CPOS == 0 && (t_addr)uptr->POS < uptr->hwmark) {
|
||
uptr->CPOS = MT_CONV1 | ch;
|
||
sim_activate(uptr, 20);
|
||
return SCPE_OK;
|
||
} else if ((uptr->CPOS & 0xc0) == MT_CONV1) {
|
||
int t = uptr->CPOS & 0x3F;
|
||
uptr->CPOS = MT_CONV2 | ch;
|
||
ch = (t << 2) | ((ch >> 4) & 03);
|
||
} else if ((uptr->CPOS & 0xc0) == MT_CONV2) {
|
||
int t = uptr->CPOS & 0xf;
|
||
uptr->CPOS = MT_CONV3 | ch;
|
||
ch = (t << 4) | ((ch >> 2) & 0xf);
|
||
} else if ((uptr->CPOS & 0xc0) == MT_CONV3) {
|
||
ch |= ((uptr->CPOS & 0x3) << 6);
|
||
uptr->CPOS = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Send character over to channel */
|
||
if (chan_write_byte(addr, &ch)) {
|
||
sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n", unit);
|
||
/* If not read whole record, skip till end */
|
||
if ((t_addr)uptr->POS < uptr->hwmark) {
|
||
/* Send dummy character to force SLI */
|
||
chan_write_byte(addr, &ch);
|
||
sim_activate(uptr, (uptr->hwmark-uptr->POS) * 20);
|
||
uptr->CMD |= MT_READDONE;
|
||
break;
|
||
}
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
|
||
}
|
||
} else {
|
||
sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02x\n",
|
||
unit, uptr->POS, ch);
|
||
if ((t_addr)uptr->POS >= uptr->hwmark) { /* In IRG */
|
||
/* Handle end of record */
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
|
||
}
|
||
} else
|
||
sim_activate(uptr, 20);
|
||
}
|
||
break;
|
||
|
||
|
||
case MT_WRITE:
|
||
/* Check if write protected */
|
||
if (sim_tape_wrp(uptr)) {
|
||
uptr->SNS |= SNS_CMDREJ;
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
|
||
}
|
||
break;
|
||
}
|
||
|
||
/* Grab data until channel has no more */
|
||
if (chan_read_byte(addr, &ch)) {
|
||
if (uptr->POS > 0 || uptr->CPOS != 0) {/* Only if data in record */
|
||
if ((uptr->flags & MTUF_9TR) == 0) {
|
||
mode = (uptr->CMD & MT_ODD) ? 0100 : 0;
|
||
if (uptr->CMD & MT_CONV) {
|
||
if ((uptr->CPOS & 0xc0) == MT_CONV1) {
|
||
int t = (uptr->CPOS & 0x3) << 4;
|
||
t ^= parity_table[t & 077] ^ mode;
|
||
mt_buffer[bufnum][uptr->POS++] = t;
|
||
} else if ((uptr->CPOS & 0xc0) == MT_CONV2) {
|
||
int t = (uptr->CPOS & 0xf) << 2;
|
||
t ^= parity_table[t & 077] ^ mode;
|
||
mt_buffer[bufnum][uptr->POS++] = t;
|
||
}
|
||
uptr->hwmark = uptr->POS;
|
||
}
|
||
}
|
||
reclen = uptr->hwmark;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Write unit=%d Block %d chars\n",
|
||
unit, reclen);
|
||
r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen);
|
||
uptr->POS = 0;
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_error(uptr, addr, r, dptr); /* Record errors */
|
||
} else {
|
||
uptr->SNS |= SNS_WCZERO; /* Write with no data */
|
||
}
|
||
} else {
|
||
if ((uptr->flags & MTUF_9TR) == 0) {
|
||
mode = (uptr->CMD & MT_ODD) ? 0100 : 0;
|
||
if (uptr->CMD & MT_TRANS)
|
||
ch = (ch & 0xf) | ((ch & 0x30) ^ 0x30);
|
||
if (uptr->CMD & MT_CONV) {
|
||
if (uptr->CPOS == 0) {
|
||
uptr->CPOS = MT_CONV1 | (ch & 0x3);
|
||
ch >>= 2;
|
||
} else if ((uptr->CPOS & 0xc0) == MT_CONV1) {
|
||
int t = uptr->CPOS & 0x3;
|
||
uptr->CPOS = MT_CONV2 | (ch & 0xf);
|
||
ch = (t << 4) | ((ch >> 4) & 0xf);
|
||
} else if ((uptr->CPOS & 0xc0) == MT_CONV2) {
|
||
int t = uptr->CPOS & 0xf;
|
||
t = (t << 2) | ((ch >> 6) & 0x3);
|
||
t ^= parity_table[t & 077] ^ mode;
|
||
mt_buffer[bufnum][uptr->POS++] = t;
|
||
uptr->CPOS = 0;
|
||
}
|
||
}
|
||
ch &= 077;
|
||
ch |= parity_table[ch] ^ mode;
|
||
}
|
||
mt_buffer[bufnum][uptr->POS++] = ch;
|
||
sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %02o\n",
|
||
unit, uptr->POS, ch);
|
||
uptr->hwmark = uptr->POS;
|
||
}
|
||
sim_activate(uptr, 20);
|
||
break;
|
||
|
||
case MT_RDBK:
|
||
if (uptr->CMD & MT_READDONE) {
|
||
uptr->CMD &= ~(MT_CMDMSK|MT_READDONE);
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* If at end of record, fill buffer */
|
||
if (BUF_EMPTY(uptr)) {
|
||
if (sim_tape_bot(uptr)) {
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
sim_debug(DEBUG_DETAIL, dptr, "Read backward unit=%d ", unit);
|
||
if ((r = sim_tape_rdrecr(uptr, &mt_buffer[bufnum][0], &reclen,
|
||
BUFFSIZE)) != MTSE_OK) {
|
||
uptr->CMD &= ~(MT_CMDMSK|MT_READDONE);
|
||
return mt_error(uptr, addr, r, dptr);
|
||
}
|
||
uptr->POS = reclen;
|
||
uptr->hwmark = reclen;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Binary Block %d chars\n", reclen);
|
||
}
|
||
|
||
ch = mt_buffer[bufnum][--uptr->POS];
|
||
if ((uptr->flags & MTUF_9TR) == 0) {
|
||
mode = (uptr->CMD & MT_ODD) ? 0 : 0100;
|
||
ch &= 077;
|
||
if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) {
|
||
uptr->SNS |= (SNS_VRC << 16) | SNS_DATCHK;
|
||
}
|
||
if (uptr->CMD & MT_TRANS)
|
||
ch = bcd_to_ebcdic[ch];
|
||
}
|
||
|
||
if (chan_write_byte(addr, &ch)) {
|
||
sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n", unit);
|
||
/* If not read whole record, skip till end */
|
||
if (uptr->POS >= 0) {
|
||
sim_activate(uptr, (uptr->POS) * 20);
|
||
uptr->CMD |= MT_READDONE;
|
||
return SCPE_OK;
|
||
}
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
|
||
}
|
||
} else {
|
||
sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n",
|
||
unit, uptr->POS, ch);
|
||
if (uptr->POS == 0) { /* In IRG */
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[bufnum] &= ~1;
|
||
if (uptr->flags & MT_BUSY) {
|
||
uptr->flags &= ~MT_BUSY;
|
||
chan_end(addr, SNS_CTLEND|SNS_CHNEND|SNS_DEVEND);
|
||
} else {
|
||
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
|
||
}
|
||
} else
|
||
sim_activate(uptr, 20);
|
||
}
|
||
break;
|
||
case 0x7:
|
||
case 0xf:
|
||
switch (cmd) {
|
||
case MT_WTM:
|
||
if (uptr->POS == 0) {
|
||
if (sim_tape_wrp(uptr)) {
|
||
uptr->SNS |= SNS_CMDREJ;
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
|
||
return SCPE_OK;
|
||
}
|
||
uptr->POS ++;
|
||
sim_activate(uptr, 500);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit);
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
r = sim_tape_wrtmk(uptr);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND);
|
||
mt_busy[bufnum] &= ~1;
|
||
}
|
||
break;
|
||
|
||
case MT_BSR:
|
||
switch (uptr->POS ) {
|
||
case 0:
|
||
if (sim_tape_bot(uptr)) {
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
|
||
return SCPE_OK;
|
||
}
|
||
uptr->POS ++;
|
||
sim_activate(uptr, 500);
|
||
break;
|
||
case 1:
|
||
uptr->POS++;
|
||
r = sim_tape_sprecr(uptr, &reclen);
|
||
sim_debug(DEBUG_DETAIL, dptr, "Backspace rec unit=%d %d ",
|
||
unit, reclen);
|
||
/* We don't set EOF on BSR */
|
||
if (r == MTSE_TMK) {
|
||
uptr->POS++;
|
||
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
|
||
sim_activate(uptr, 50);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d \n", reclen);
|
||
sim_activate(uptr, 10 + (10 * reclen));
|
||
}
|
||
break;
|
||
case 2:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND);
|
||
mt_busy[bufnum] &= ~1;
|
||
break;
|
||
case 3:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITEXP);
|
||
mt_busy[bufnum] &= ~1;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case MT_BSF:
|
||
switch(uptr->POS) {
|
||
case 0:
|
||
if (sim_tape_bot(uptr)) {
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[bufnum] &= ~1;
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
|
||
break;
|
||
}
|
||
uptr->POS ++;
|
||
sim_activate(uptr, 500);
|
||
break;
|
||
case 1:
|
||
r = sim_tape_sprecr(uptr, &reclen);
|
||
sim_debug(DEBUG_DETAIL, dptr, "Backspace file unit=%d %d\n",
|
||
unit, reclen);
|
||
if (r == MTSE_TMK) {
|
||
uptr->POS++;
|
||
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
|
||
sim_activate(uptr, 50);
|
||
} else if (r == MTSE_BOT) {
|
||
uptr->POS+= 2;
|
||
sim_activate(uptr, 50);
|
||
} else {
|
||
sim_activate(uptr, 10 + (10 * reclen));
|
||
}
|
||
break;
|
||
case 2:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND);
|
||
mt_busy[bufnum] &= ~1;
|
||
break;
|
||
case 3:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
|
||
mt_busy[bufnum] &= ~1;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case MT_FSR:
|
||
switch(uptr->POS) {
|
||
case 0:
|
||
uptr->POS ++;
|
||
sim_activate(uptr, 500);
|
||
break;
|
||
case 1:
|
||
uptr->POS++;
|
||
r = sim_tape_sprecf(uptr, &reclen);
|
||
sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d %d ", unit, reclen);
|
||
if (r == MTSE_TMK) {
|
||
uptr->POS = 3;
|
||
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
|
||
sim_activate(uptr, 50);
|
||
} else if (r == MTSE_EOM) {
|
||
uptr->POS = 2;
|
||
sim_activate(uptr, 50);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen);
|
||
sim_activate(uptr, 10 + (10 * reclen));
|
||
}
|
||
break;
|
||
case 2:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND);
|
||
mt_busy[bufnum] &= ~1;
|
||
break;
|
||
case 3:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITEXP);
|
||
mt_busy[bufnum] &= ~1;
|
||
break;
|
||
case 4:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
|
||
mt_busy[bufnum] &= ~1;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case MT_FSF:
|
||
switch(uptr->POS) {
|
||
case 0:
|
||
uptr->POS ++;
|
||
sim_activate(uptr, 500);
|
||
break;
|
||
case 1:
|
||
r = sim_tape_sprecf(uptr, &reclen);
|
||
sim_debug(DEBUG_DETAIL, dptr, "Skip frec unit=%d %d ", unit, reclen);
|
||
if (r == MTSE_TMK) {
|
||
uptr->POS++;
|
||
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
|
||
sim_activate(uptr, 50);
|
||
} else if (r == MTSE_EOM) {
|
||
uptr->POS+= 2;
|
||
sim_activate(uptr, 50);
|
||
} else {
|
||
sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen);
|
||
sim_activate(uptr, 10 + (10 * reclen));
|
||
}
|
||
break;
|
||
case 2:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND);
|
||
mt_busy[bufnum] &= ~1;
|
||
sim_debug(DEBUG_DETAIL, dptr, "Skip done unit=%d\n", unit);
|
||
break;
|
||
case 3:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
|
||
mt_busy[bufnum] &= ~1;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case MT_ERG:
|
||
switch (uptr->POS) {
|
||
case 0:
|
||
if (sim_tape_wrp(uptr)) {
|
||
uptr->SNS |= SNS_CMDREJ;
|
||
uptr->CMD &= ~MT_CMDMSK;
|
||
mt_busy[bufnum] &= ~1;
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
|
||
} else {
|
||
uptr->POS ++;
|
||
sim_activate(uptr, 500);
|
||
}
|
||
break;
|
||
case 1:
|
||
sim_debug(DEBUG_DETAIL, dptr, "Erase unit=%d\n", unit);
|
||
r = sim_tape_wrgap(uptr, 35);
|
||
sim_activate(uptr, 5000);
|
||
uptr->POS++;
|
||
break;
|
||
case 2:
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->flags &= ~MT_BUSY;
|
||
set_devattn(addr, SNS_DEVEND);
|
||
mt_busy[bufnum] &= ~1;
|
||
}
|
||
break;
|
||
|
||
case MT_REW:
|
||
mt_busy[bufnum] &= ~1;
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->CMD |= MT_CMDREW;
|
||
sim_activate(uptr, 1000 + (20 * uptr->pos));
|
||
set_devattn(addr, SNS_DEVEND);
|
||
break;
|
||
|
||
case MT_RUN:
|
||
mt_busy[bufnum] &= ~1;
|
||
uptr->CMD &= ~(MT_CMDMSK);
|
||
uptr->CMD |= MT_CMDRUN;
|
||
sim_activate(uptr, 1000 + (20 * uptr->pos));
|
||
set_devattn(addr, SNS_DEVEND);
|
||
break;
|
||
}
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
void
|
||
mt_ini(UNIT * uptr, t_bool f)
|
||
{
|
||
DEVICE *dptr = find_dev_from_unit(uptr);
|
||
|
||
uptr->CMD &= UNIT_ADDR_MASK;
|
||
if ((uptr->flags & MTUF_9TR) == 0)
|
||
uptr->CMD |= MT_ODD|MT_CONV|MT_MDEN_800;
|
||
mt_busy[GET_DEV_BUF(dptr->flags)] = 0;
|
||
}
|
||
|
||
t_stat
|
||
mt_boot(int32 unit_num, DEVICE * dptr)
|
||
{
|
||
UNIT *uptr = &dptr->units[unit_num];
|
||
|
||
if ((uptr->flags & UNIT_ATT) == 0)
|
||
return SCPE_UNATT; /* attached? */
|
||
if ((uptr->flags & MTUF_9TR) == 0) {
|
||
uptr->CMD &= UNIT_ADDR_MASK;
|
||
uptr->CMD |= MT_ODD|MT_CONV|MT_MDEN_800;
|
||
}
|
||
return chan_boot(GET_UADDR(uptr->CMD), dptr);
|
||
}
|
||
|
||
t_stat
|
||
mt_attach(UNIT * uptr, CONST char *file)
|
||
{
|
||
uint16 addr = GET_UADDR(uptr->CMD);
|
||
t_stat r;
|
||
|
||
if ((r = sim_tape_attach_ex(uptr, file, 0, 0)) != SCPE_OK)
|
||
return r;
|
||
set_devattn(addr, SNS_DEVEND);
|
||
uptr->CMD &= UNIT_ADDR_MASK;
|
||
if ((uptr->flags & MTUF_9TR) == 0) {
|
||
uptr->CMD |= MT_ODD | MT_CONV | MT_MDEN_800;
|
||
}
|
||
uptr->POS = 0;
|
||
uptr->SNS = 0;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
mt_detach(UNIT * uptr)
|
||
{
|
||
uptr->CMD &= UNIT_ADDR_MASK;
|
||
uptr->POS = 0;
|
||
uptr->SNS = 0;
|
||
return sim_tape_detach(uptr);
|
||
}
|
||
|
||
t_stat mt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
||
{
|
||
fprintf (st, "2400 Magnetic Tape\n\n");
|
||
fprint_set_help (st, dptr);
|
||
fprint_show_help (st, dptr);
|
||
fprintf (st, "\nThe type options can be used only when a unit is not attached to a file. The\n");
|
||
fprintf (st, "bad block option can be used only when a unit is attached to a file.\n");
|
||
fprintf (st, "The magtape supports the BOOT command.\n");
|
||
sim_tape_attach_help (st, dptr, uptr, flag, cptr);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *mt_description (DEVICE *dptr)
|
||
{
|
||
return "2400 magnetic tape" ;
|
||
}
|
||
|
||
#endif
|