1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-04-25 11:42:02 +00:00
Files
rcornwell.sims/IBM360/ibm360_mt.c
Richard Cornwell 883dd27f9f IBM360: Major update. MVT now can be built.
CDR:  Clean up to return correct status on EOF and empty deck.
   CHAN: Fixed problems with storage key support.
   CHAN: Proper handling of error on initial connect to device.
   CPU:  Fixed errors in handling of storage key protection.
   CPU:  Added aligned data access.
   CPU:  Floating point code starting to work. Still some errors.
   DASD: Corrected sense data for 2311 and 2314 drives for CP/67.
   MT:   Cleaned up proper handling of unattached units.
   SYS:  Fixed bug in deposit.
2019-01-21 22:57:36 -05:00

919 lines
34 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ibm360_mt.c: IBM 360 2400 Magnetic tape controller
Copyright (c) 2017, 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 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_MODE 0x03 /* Mode command */
#define MT_MODEMSK 0x07 /* Mode Mask */
#define MT_MDEN_200 0x00 /* 200 BPI mode 7 track only */
#define MT_MDEN_556 0x40 /* 556 BPI mode 7 track only */
#define MT_MDEN_800 0x80 /* 800 BPI mode 7 track only */
#define MT_MDEN_1600 0xc0 /* 1600 BPI mode 9 track only */
#define MT_MDEN_MSK 0xc0 /* 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 0x003f /* Command being run */
#define MT_READDONE 0x0400 /* Read finished, end channel */
#define MT_MARK 0x0800 /* Sensed tape mark in move command */
#define MT_ODD 0x1000 /* Odd parity */
#define MT_TRANS 0x2000 /* Translation turned on ignored 9 track */
#define MT_CONV 0x4000 /* Data converter on ignored 9 track */
#define MT_BUSY 0x8000 /* Flag to send a CUE */
/* 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 0xc0 /* 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
uint8 mt_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) ;
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_boot(int32, 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},
{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, NULL, 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, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
&mta_dib, DEV_BUF_NUM(0) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug
};
#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, NULL, 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, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
&mtb_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug
};
#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_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) {
uint16 addr = GET_UADDR(uptr->u3);
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units);
uint8 ch;
if (mt_busy[GET_DEV_BUF(dptr->flags)] != 0 || (uptr->u3 & MT_CMDMSK) != 0) {
sim_debug(DEBUG_CMD, dptr, "CMD busy unit=%d %x\n", unit, cmd);
uptr->flags |= MT_BUSY; /* Flag we need to send CUE */
return SNS_BSY;
}
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->u5 = 0;
uptr->u5 |= SNS_TUASTA << 8;
if ((uptr->flags & MTUF_9TR) == 0)
uptr->u5 |= (SNS_7TRACK << 8);
if (sim_tape_wrp(uptr))
uptr->u5 |= (SNS_WRP << 8);
if (sim_tape_bot(uptr))
uptr->u5 |= (SNS_LOAD << 8);
/* Fall through */
case 0x4: /* Sense */
uptr->u3 &= ~(MT_CMDMSK);
uptr->u3 |= cmd & MT_CMDMSK;
sim_activate(uptr, 1000); /* Start unit off */
CLR_BUF(uptr);
uptr->u4 = 0;
uptr->u6 = 0;
mt_busy[GET_DEV_BUF(dptr->flags)] = 1;
if ((cmd & 0x7) == 0x7) /* Quick end channel on control */
return SNS_CHNEND;
return 0;
case 0x3: /* Control */
case 0xb: /* Control */
if ((uptr->flags & UNIT_ATT) == 0) {
uptr->u5 |= SNS_INTVENT;
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
}
if ((uptr->flags & MTUF_9TR) == 0) {
uptr->u5 |= (SNS_7TRACK << 8);
if ((cmd & 0xc0) == 0xc0) {
uptr->u5 |= SNS_CMDREJ;
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
}
switch((cmd >> 3) & 07) {
case 0: /* NOP */
case 1: /* Diagnostics */
case 3:
return SNS_CHNEND|SNS_DEVEND ;
case 2: /* Reset condition */
uptr->u3 &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
uptr->u3 |= (cmd & MT_MDEN_MSK) | MT_ODD | MT_CONV;
break;
case 4:
uptr->u3 &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
uptr->u3 |= (cmd & MT_MDEN_MSK);
break;
case 5:
uptr->u3 &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
uptr->u3 |= (cmd & MT_MDEN_MSK) | MT_TRANS;
break;
case 6:
uptr->u3 &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
uptr->u3 |= (cmd & MT_MDEN_MSK) | MT_ODD;
break;
case 7:
uptr->u3 &= ~(MT_ODD|MT_TRANS|MT_CONV|MT_MDEN_MSK);
uptr->u3 |= (cmd & MT_MDEN_MSK) | MT_ODD | MT_TRANS;
break;
}
} else {
uptr->u3 &= ~MT_MDEN_MSK;
if (cmd & 0x8)
uptr->u3 |= MT_MDEN_800;
else
uptr->u3 |= MT_MDEN_1600;
}
uptr->u5 = 0;
break;
case 0x0: /* Status */
break;
default: /* invalid command */
uptr->u5 |= SNS_CMDREJ;
break;
}
if (uptr->u5 & 0xff)
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
return SNS_CHNEND|SNS_DEVEND;
}
/* Map simH errors into machine errors */
t_stat mt_error(UNIT * uptr, uint16 addr, t_stat r, DEVICE * dptr)
{
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, SNS_CHNEND|SNS_DEVEND|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 ");
uptr->u5 = SNS_EQUCHK;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP);
return SCPE_OK;
}
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
return SCPE_OK;
}
/* Handle processing of tape requests. */
t_stat mt_srv(UNIT * uptr)
{
uint16 addr = GET_UADDR(uptr->u3);
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units);
int cmd = uptr->u3 & 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->u5 |= SNS_INTVENT;
if (cmd != MT_SENSE) {
uptr->u3 &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
return SCPE_OK;
}
}
switch (cmd) {
case 0: /* No command, stop tape */
sim_debug(DEBUG_DETAIL, dptr, "Idle unit=%d\n", unit);
break;
case MT_SENSE:
ch = uptr->u5 & 0xff;
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 1 %x\n", unit, ch);
chan_write_byte(addr, &ch) ;
ch = (uptr->u5 >> 8) & 0xff;
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 2 %x\n", unit, ch);
chan_write_byte(addr, &ch) ;
ch = 0xc0;
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 3 %x\n", unit, ch);
chan_write_byte(addr, &ch) ;
ch = (uptr->u5 >> 16) & 0xff;
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 4 %x\n", unit, ch);
chan_write_byte(addr, &ch) ;
ch = 0;
chan_write_byte(addr, &ch) ;
chan_write_byte(addr, &ch);
uptr->u3 &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
case MT_READ:
if (uptr->u3 & MT_READDONE) {
uptr->u3 &= ~(MT_CMDMSK|MT_READDONE);
mt_busy[bufnum] &= ~1;
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->u3 &= ~(MT_CMDMSK|MT_READDONE);
return mt_error(uptr, addr, r, dptr);
}
uptr->u4 = 0;
uptr->hwmark = reclen;
sim_debug(DEBUG_DETAIL, dptr, "Block %d chars\n", reclen);
}
ch = mt_buffer[bufnum][uptr->u4++];
/* if we are a 7track tape, handle conversion */
if ((uptr->flags & MTUF_9TR) == 0) {
mode = (uptr->u3 & 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->u4-1, ch);
uptr->u5 |= (SNS_VRC << 16) | SNS_DATCHK;
}
ch &= 077;
if (uptr->u3 & MT_TRANS)
ch = bcd_to_ebcdic[ch];
if (uptr->u3 & MT_CONV) {
sim_debug(DEBUG_DATA, dptr, "Read raw data unit=%d %d %02x %02x\n",
unit, uptr->u4, ch, uptr->u6);
if (uptr->u6 == 0 && uptr->u4 < uptr->hwmark) {
uptr->u6 = MT_CONV1 | ch;
sim_activate(uptr, 20);
return SCPE_OK;
} else if ((uptr->u6 & 0xc0) == MT_CONV1) {
int t = uptr->u6 & 0x3F;
uptr->u6 = MT_CONV2 | ch;
ch = (t << 2) | ((ch >> 4) & 03);
} else if ((uptr->u6 & 0xc0) == MT_CONV2) {
int t = uptr->u6 & 0xf;
uptr->u6 = MT_CONV3 | ch;
ch = (t << 4) | ((ch >> 2) & 0xf);
} else if ((uptr->u6 & 0xc0) == MT_CONV3) {
ch |= ((uptr->u6 & 0x3) << 6);
uptr->u6 = 0;
}
}
}
/* Send character over to channel */
if (chan_write_byte(addr, &ch)) {
sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n\r", unit);
/* If not read whole record, skip till end */
if (uptr->u4 < uptr->hwmark) {
/* Send dummy character to force SLI */
chan_write_byte(addr, &ch);
sim_activate(uptr, (uptr->hwmark-uptr->u4) * 20);
uptr->u3 |= MT_READDONE;
break;
}
uptr->u3 &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_DEVEND);
} else {
sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02x\n\r",
unit, uptr->u4, ch);
if (uptr->u4 >= uptr->hwmark) { /* In IRG */
/* Handle end of record */
uptr->u3 &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
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->u5 |= SNS_CMDREJ;
uptr->u3 &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
break;
}
/* Grab data until channel has no more */
if (chan_read_byte(addr, &ch)) {
if (uptr->u4 > 0) { /* Only if data in record */
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->u4 = 0;
uptr->u3 &= ~MT_CMDMSK;
mt_error(uptr, addr, r, dptr); /* Record errors */
} else {
uptr->u5 |= SNS_WCZERO; /* Write with no data */
}
} else {
if ((uptr->flags & MTUF_9TR) == 0) {
mode = (uptr->u3 & MT_ODD) ? 0 : 0100;
if (uptr->u3 & MT_TRANS)
ch = (ch & 0xf) | ((ch & 0x30) ^ 0x30);
if (uptr->u3 & MT_CONV) {
if (uptr->u6 == 0) {
uptr->u6 = MT_CONV1 | (ch & 0x3);
ch >>= 2;
} else if ((uptr->u6 & 0xc0) == MT_CONV1) {
int t = uptr->u6 & 0x3;
uptr->u6 = MT_CONV2 | (ch & 0xf);
ch = (t << 4) | ((ch >> 4) & 0xf);
} else if ((uptr->u6 & 0xc0) == MT_CONV2) {
int t = uptr->u6 & 0xf;
ch = (t << 2) | ((ch >> 6) & 0x3);
ch ^= parity_table[ch & 077] ^ mode;
mt_buffer[bufnum][uptr->u4++] = ch;
uptr->u6 = 0;
}
}
ch &= 077;
ch |= parity_table[ch] ^ mode;
}
mt_buffer[bufnum][uptr->u4++] = ch;
sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %02o\n\r",
unit, uptr->u4, ch);
uptr->hwmark = uptr->u4;
break;
}
sim_activate(uptr, 20);
break;
case MT_RDBK:
if (uptr->u3 & MT_READDONE) {
uptr->u3 &= ~(MT_CMDMSK|MT_READDONE);
mt_busy[bufnum] &= ~1;
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->u3 &= ~MT_CMDMSK;
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
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->u3 &= ~(MT_CMDMSK|MT_READDONE);
return mt_error(uptr, addr, r, dptr);
}
uptr->u4 = reclen;
uptr->hwmark = reclen;
sim_debug(DEBUG_DETAIL, dptr, "Binary Block %d chars\n", reclen);
}
ch = mt_buffer[bufnum][--uptr->u4];
if ((uptr->flags & MTUF_9TR) == 0) {
mode = (uptr->u3 & MT_ODD) ? 0 : 0100;
ch &= 077;
if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) {
uptr->u5 |= (SNS_VRC << 16) | SNS_DATCHK;
}
if (uptr->u3 & MT_TRANS)
ch = bcd_to_ebcdic[ch];
if (uptr->u3 & MT_CONV) {
if (uptr->u6 == 0 && uptr->u4 < uptr->hwmark) {
uptr->u6 = MT_CONV1 | ch;
sim_activate(uptr, 20);
return SCPE_OK;
} else if ((uptr->u6 & 0xc0) == MT_CONV1) {
int t = uptr->u6 & 0x3F;
uptr->u6 = MT_CONV2 | ch;
ch = t | ((ch << 6) & 0xc0);
} else if ((uptr->u6 & 0xc0) == MT_CONV2) {
int t = uptr->u6 & 0x3C;
uptr->u6 = MT_CONV3 | ch;
ch = (t >> 2) | ((ch << 4) & 0xf0);
} else if ((uptr->u6 & 0xc0) == MT_CONV3) {
ch |= ((uptr->u6 & 0x30) >> 4);
uptr->u6 = 0;
}
}
}
if (chan_write_byte(addr, &ch)) {
sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n\r", unit);
/* If not read whole record, skip till end */
if (uptr->u4 >= 0) {
sim_activate(uptr, (uptr->u4) * 20);
uptr->u3 |= MT_READDONE;
return SCPE_OK;
}
uptr->u3 &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
} else {
sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n\r",
unit, uptr->u4, ch);
if (uptr->u4 == 0) { /* In IRG */
uptr->u3 &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
} else
sim_activate(uptr, 20);
}
break;
case MT_WTM:
if (uptr->u4 == 0) {
if (sim_tape_wrp(uptr)) {
uptr->u5 |= SNS_CMDREJ;
uptr->u3 &= ~MT_CMDMSK;
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
return SCPE_OK;
}
uptr->u4 ++;
sim_activate(uptr, 500);
} else {
sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit);
uptr->u3 &= ~(MT_CMDMSK);
r = sim_tape_wrtmk(uptr);
set_devattn(addr, SNS_DEVEND);
mt_busy[bufnum] &= ~1;
}
break;
case MT_BSR:
switch (uptr->u4 ) {
case 0:
if (sim_tape_bot(uptr)) {
uptr->u3 &= ~MT_CMDMSK;
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
return SCPE_OK;
}
uptr->u4 ++;
sim_activate(uptr, 500);
break;
case 1:
uptr->u4++;
sim_debug(DEBUG_DETAIL, dptr, "Backspace rec unit=%d ", unit);
r = sim_tape_sprecr(uptr, &reclen);
/* We don't set EOF on BSR */
if (r == MTSE_TMK) {
uptr->u4++;
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->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND);
mt_busy[bufnum] &= ~1;
break;
case 3:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND|SNS_UNITEXP);
mt_busy[bufnum] &= ~1;
break;
}
break;
case MT_BSF:
switch(uptr->u4) {
case 0:
if (sim_tape_bot(uptr)) {
uptr->u3 &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
break;
}
uptr->u4 ++;
sim_activate(uptr, 500);
break;
case 1:
sim_debug(DEBUG_DETAIL, dptr, "Backspace file unit=%d\n", unit);
r = sim_tape_sprecr(uptr, &reclen);
if (r == MTSE_TMK) {
uptr->u4++;
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
sim_activate(uptr, 50);
} else if (r == MTSE_BOT) {
uptr->u4+= 2;
sim_activate(uptr, 50);
} else {
sim_activate(uptr, 10 + (10 * reclen));
}
break;
case 2:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND|SNS_UNITEXP);
mt_busy[bufnum] &= ~1;
break;
case 3:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
mt_busy[bufnum] &= ~1;
break;
}
break;
case MT_FSR:
switch(uptr->u4) {
case 0:
uptr->u4 ++;
sim_activate(uptr, 500);
break;
case 1:
uptr->u4++;
sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d ", unit);
r = sim_tape_sprecf(uptr, &reclen);
if (r == MTSE_TMK) {
uptr->u4 = 3;
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
sim_activate(uptr, 50);
} else if (r == MTSE_EOM) {
uptr->u4 = 4;
sim_activate(uptr, 50);
} else {
sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen);
sim_activate(uptr, 10 + (10 * reclen));
}
break;
case 2:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND);
mt_busy[bufnum] &= ~1;
break;
case 3:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND|SNS_UNITEXP);
mt_busy[bufnum] &= ~1;
break;
case 4:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
mt_busy[bufnum] &= ~1;
break;
}
break;
case MT_FSF:
switch(uptr->u4) {
case 0:
uptr->u4 ++;
sim_activate(uptr, 500);
break;
case 1:
sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d ", unit);
r = sim_tape_sprecf(uptr, &reclen);
if (r == MTSE_TMK) {
uptr->u4++;
sim_debug(DEBUG_DETAIL, dptr, "MARK\n");
sim_activate(uptr, 50);
} else if (r == MTSE_EOM) {
uptr->u4+= 2;
sim_activate(uptr, 50);
} else {
sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen);
sim_activate(uptr, 10 + (10 * reclen));
}
break;
case 2:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND);
mt_busy[bufnum] &= ~1;
sim_debug(DEBUG_DETAIL, dptr, "Skip done unit=%d\n", unit);
break;
case 3:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
mt_busy[bufnum] &= ~1;
break;
}
break;
case MT_ERG:
switch (uptr->u4) {
case 0:
if (sim_tape_wrp(uptr)) {
uptr->u5 |= SNS_CMDREJ;
uptr->u3 &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
set_devattn(addr, SNS_DEVEND|SNS_UNITCHK);
} else {
uptr->u4 ++;
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->u4++;
break;
case 2:
uptr->u3 &= ~(MT_CMDMSK);
set_devattn(addr, SNS_DEVEND);
mt_busy[bufnum] &= ~1;
}
break;
case MT_REW:
if (uptr->u4 == 0) {
uptr->u4 ++;
sim_activate(uptr, 30000);
mt_busy[bufnum] &= ~1;
} else {
sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d\n", unit);
uptr->u3 &= ~(MT_CMDMSK);
r = sim_tape_rewind(uptr);
set_devattn(addr, SNS_DEVEND);
}
break;
case MT_RUN:
if (uptr->u4 == 0) {
uptr->u4 ++;
mt_busy[bufnum] &= ~1;
sim_activate(uptr, 30000);
} else {
sim_debug(DEBUG_DETAIL, dptr, "Unload unit=%d\n", unit);
uptr->u3 &= ~(MT_CMDMSK);
r = sim_tape_detach(uptr);
}
break;
}
return SCPE_OK;
}
void
mt_ini(UNIT * uptr, t_bool f)
{
DEVICE *dptr = find_dev_from_unit(uptr);
uptr->u3 &= UNIT_ADDR_MASK;
if ((uptr->flags & MTUF_9TR) == 0)
uptr->u3 |= MT_ODD|MT_CONV|MT_MDEN_800;
mt_busy[GET_DEV_BUF(dptr->flags)] = 0;
}
t_stat
mt_reset(DEVICE * dptr)
{
return SCPE_OK;
}
t_stat
mt_attach(UNIT * uptr, CONST char *file)
{
uint16 addr = GET_UADDR(uptr->u3);
t_stat r;
if ((r = sim_tape_attach_ex(uptr, file, 0, 0)) != SCPE_OK)
return r;
set_devattn(addr, SNS_DEVEND);
uptr->u3 &= UNIT_ADDR_MASK;
uptr->u4 = 0;
uptr->u5 = 0;
return SCPE_OK;
}
t_stat
mt_detach(UNIT * uptr)
{
uptr->u3 &= UNIT_ADDR_MASK;
return sim_tape_detach(uptr);
}
t_stat
mt_boot(int32 unit_num, DEVICE * dptr)
{
UNIT *uptr = &dptr->units[unit_num];
t_stat r;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_UNATT; /* attached? */
if ((uptr->flags & MTUF_9TR) == 0) {
uptr->u3 &= UNIT_ADDR_MASK;
uptr->u3 |= MT_ODD|MT_CONV|MT_MDEN_800;
}
return chan_boot(GET_UADDR(uptr->u3), dptr);
}
#endif