The makefile now works for Linux and most Unix's. Howevr, for Solaris and MacOS, you must first export the OSTYPE environment variable: > export OSTYPE > make Otherwise, you will get build errors. 1. New Features 1.1 3.8-0 1.1.1 SCP and Libraries - BREAK, NOBREAK, and SHOW BREAK with no argument will set, clear, and show (respectively) a breakpoint at the current PC. 1.1.2 GRI - Added support for the GRI-99 processor. 1.1.3 HP2100 - Added support for the BACI terminal interface. - Added support for RTE OS/VMA/EMA, SIGNAL, VIS firmware extensions. 1.1.4 Nova - Added support for 64KW memory (implemented in third-party CPU's). 1.1.5 PDP-11 - Added support for DC11, RC11, KE11A, KG11A. - Added modem control support for DL11. - Added ASCII character support for all 8b devices. 1.2 3.8-1 1.2.1 SCP and libraries - Added capability to set line connection order for terminal multiplexers. 1.2.2 HP2100 - Added support for 12620A/12936A privileged interrupt fence. - Added support for 12792C eight-channel asynchronous multiplexer. 2. Bugs Fixed Please see the revision history on http://simh.trailing-edge.com or in the source module sim_rev.h.
612 lines
24 KiB
C
612 lines
24 KiB
C
/* pdp11_ta.c: PDP-11 cassette tape simulator
|
|
|
|
Copyright (c) 2007-2008, Robert M Supnik
|
|
|
|
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
|
|
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ATAION OF CONTRATA, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNETAION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
ta TA11/TU60 cassette tape
|
|
|
|
06-Aug-07 RMS Foward op at BOT skips initial file gap
|
|
|
|
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.
|
|
|
|
Cassette format differs in one very significant way: it has file gaps
|
|
rather than file marks. If the controller spaces or reads into a file
|
|
gap and then reverses direction, the file gap is not seen again. This
|
|
is in contrast to magnetic tapes, where the file mark is a character
|
|
sequence and is seen again if direction is reversed. In addition,
|
|
cassettes have an initial file gap which is automatically skipped on
|
|
forward operations from beginning of tape.
|
|
*/
|
|
|
|
#include "pdp11_defs.h"
|
|
#include "sim_tape.h"
|
|
|
|
#define TA_NUMDR 2 /* #drives */
|
|
#define FNC u3 /* unit function */
|
|
#define UST u4 /* unit status */
|
|
#define TA_SIZE 93000 /* chars/tape */
|
|
#define TA_MAXFR (TA_SIZE) /* max record lnt */
|
|
|
|
/* Control/status - TACS */
|
|
|
|
#define TACS_ERR (1 << CSR_V_ERR) /* error */
|
|
#define TACS_CRC 0040000 /* CRC */
|
|
#define TACS_BEOT 0020000 /* BOT/EOT */
|
|
#define TACS_WLK 0010000 /* write lock */
|
|
#define TACS_EOF 0004000 /* end file */
|
|
#define TACS_TIM 0002000 /* timing */
|
|
#define TACS_EMP 0001000 /* empty */
|
|
#define TACS_V_UNIT 8 /* unit */
|
|
#define TACS_M_UNIT (TA_NUMDR - 1)
|
|
#define TACS_UNIT (TACS_M_UNIT << TACS_V_UNIT)
|
|
#define TACS_TR (1 << CSR_V_DONE) /* transfer req */
|
|
#define TACS_IE (1 << CSR_V_IE) /* interrupt enable */
|
|
#define TACS_RDY 0000040 /* ready */
|
|
#define TACS_ILBS 0000020 /* start CRC */
|
|
#define TACS_V_FNC 1 /* function */
|
|
#define TACS_M_FNC 07
|
|
#define TACS_WFG 00
|
|
#define TACS_WRITE 01
|
|
#define TACS_READ 02
|
|
#define TACS_SRF 03
|
|
#define TACS_SRB 04
|
|
#define TACS_SFF 05
|
|
#define TACS_SFB 06
|
|
#define TACS_REW 07
|
|
#define TACS_2ND 010
|
|
#define TACS_3RD 030
|
|
#define TACS_FNC (TACS_M_FNC << TACS_V_FNC)
|
|
#define TACS_GO (1 << CSR_V_GO) /* go */
|
|
#define TACS_W (TACS_UNIT|TACS_IE|TACS_ILBS|TACS_FNC)
|
|
#define TACS_XFRERR (TACS_ERR|TACS_CRC|TACS_WLK|TACS_EOF|TACS_TIM)
|
|
#define GET_UNIT(x) (((x) >> TACS_V_UNIT) & TACS_M_UNIT)
|
|
#define GET_FNC(x) (((x) >> TACS_V_FNC) & TACS_M_FNC)
|
|
|
|
/* Function code flags */
|
|
|
|
#define OP_WRI 01 /* op is a write */
|
|
#define OP_REV 02 /* op is rev motion */
|
|
#define OP_FWD 04 /* op is fwd motion */
|
|
|
|
/* Unit status flags */
|
|
|
|
#define UST_REV (OP_REV) /* last op was rev */
|
|
#define UST_GAP 01 /* last op hit gap */
|
|
|
|
extern int32 int_req[IPL_HLVL];
|
|
extern FILE *sim_deb;
|
|
|
|
uint32 ta_cs = 0; /* control/status */
|
|
uint32 ta_idb = 0; /* input data buf */
|
|
uint32 ta_odb = 0; /* output data buf */
|
|
uint32 ta_write = 0; /* TU60 write flag */
|
|
uint32 ta_bptr = 0; /* buf ptr */
|
|
uint32 ta_blnt = 0; /* buf length */
|
|
int32 ta_stime = 1000; /* start time */
|
|
int32 ta_ctime = 100; /* char latency */
|
|
uint32 ta_stopioe = 1; /* stop on error */
|
|
uint8 *ta_xb = NULL; /* transfer buffer */
|
|
static uint8 ta_fnc_tab[TACS_M_FNC + 1] = {
|
|
OP_WRI|OP_FWD, OP_WRI|OP_FWD, OP_FWD, OP_REV,
|
|
OP_REV , OP_FWD, OP_FWD, 0
|
|
};
|
|
|
|
DEVICE ta_dev;
|
|
t_stat ta_rd (int32 *data, int32 PA, int32 access);
|
|
t_stat ta_wr (int32 data, int32 PA, int32 access);
|
|
t_stat ta_svc (UNIT *uptr);
|
|
t_stat ta_reset (DEVICE *dptr);
|
|
t_stat ta_attach (UNIT *uptr, char *cptr);
|
|
t_stat ta_detach (UNIT *uptr);
|
|
void ta_go (void);
|
|
t_stat ta_map_err (UNIT *uptr, t_stat st);
|
|
UNIT *ta_busy (void);
|
|
void ta_set_tr (void);
|
|
uint32 ta_updsta (UNIT *uptr);
|
|
uint32 ta_crc (uint8 *buf, uint32 cnt);
|
|
|
|
/* TA data structures
|
|
|
|
ta_dev TA device descriptor
|
|
ta_unit TA unit list
|
|
ta_reg TA register list
|
|
ta_mod TA modifier list
|
|
*/
|
|
|
|
DIB ta_dib = {
|
|
IOBA_TA, IOLN_TA, &ta_rd, &ta_wr,
|
|
1, IVCL (TA), VEC_TA, { NULL }
|
|
};
|
|
|
|
UNIT ta_unit[] = {
|
|
{ UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) },
|
|
{ UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) },
|
|
};
|
|
|
|
REG ta_reg[] = {
|
|
{ ORDATA (TACS, ta_cs, 16) },
|
|
{ ORDATA (TAIDB, ta_idb, 8) },
|
|
{ ORDATA (TAODB, ta_odb, 8) },
|
|
{ FLDATA (WRITE, ta_write, 0) },
|
|
{ FLDATA (INT, IREQ (TA), INT_V_TA) },
|
|
{ FLDATA (ERR, ta_cs, CSR_V_ERR) },
|
|
{ FLDATA (TR, ta_cs, CSR_V_DONE) },
|
|
{ FLDATA (IE, ta_cs, CSR_V_IE) },
|
|
{ DRDATA (BPTR, ta_bptr, 17) },
|
|
{ DRDATA (BLNT, ta_blnt, 17) },
|
|
{ DRDATA (STIME, ta_stime, 24), PV_LEFT + REG_NZ },
|
|
{ DRDATA (CTIME, ta_ctime, 24), PV_LEFT + REG_NZ },
|
|
{ FLDATA (STOP_IOE, ta_stopioe, 0) },
|
|
{ URDATA (UFNC, ta_unit[0].FNC, 8, 5, 0, TA_NUMDR, 0), REG_HRO },
|
|
{ URDATA (UST, ta_unit[0].UST, 8, 2, 0, TA_NUMDR, 0), REG_HRO },
|
|
{ URDATA (POS, ta_unit[0].pos, 10, T_ADDR_W, 0,
|
|
TA_NUMDR, PV_LEFT | REG_RO) },
|
|
{ ORDATA (DEVADDR, ta_dib.ba, 32), REG_HRO },
|
|
{ ORDATA (DEVVEC, ta_dib.vec, 16), REG_HRO },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB ta_mod[] = {
|
|
{ MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },
|
|
{ MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },
|
|
// { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
|
|
// &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
|
|
{ MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL,
|
|
NULL, &sim_tape_show_capac, NULL },
|
|
{ MTAB_XTD|MTAB_VDV, IOLN_TA, "ADDRESS", "ADDRESS",
|
|
&set_addr, &show_addr, NULL },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
|
|
&set_vec, &show_vec, NULL },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE ta_dev = {
|
|
"TA", ta_unit, ta_reg, ta_mod,
|
|
TA_NUMDR, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &ta_reset,
|
|
NULL, &ta_attach, &ta_detach,
|
|
&ta_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG
|
|
};
|
|
|
|
/* I/O dispatch routines, I/O addresses 17777500 - 17777503
|
|
|
|
17777500 TACS read/write
|
|
17777502 TADB read/write
|
|
*/
|
|
|
|
t_stat ta_rd (int32 *data, int32 PA, int32 access)
|
|
{
|
|
switch ((PA >> 1) & 01) { /* decode PA<1> */
|
|
|
|
case 0: /* TACSR */
|
|
*data = ta_updsta (NULL); /* update status */
|
|
break;
|
|
|
|
case 1: /* TADB */
|
|
*data = ta_idb; /* return byte */
|
|
ta_cs &= ~TACS_TR; /* clear tra req */
|
|
ta_updsta (NULL);
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ta_wr (int32 data, int32 PA, int32 access)
|
|
{
|
|
switch ((PA >> 1) & 01) { /* decode PA<1> */
|
|
|
|
case 0: /* TACS */
|
|
if (access == WRITEB) data = (PA & 1)? /* byte write? */
|
|
(ta_cs & 0377) | (data << 8): /* merge old */
|
|
(ta_cs & ~0377) | data;
|
|
ta_cs = (ta_cs & ~TACS_W) | (data & TACS_W); /* merge new */
|
|
if ((data & CSR_GO) && !ta_busy ()) /* go, not busy? */
|
|
ta_go (); /* start operation */
|
|
if (ta_cs & TACS_ILBS) /* ILBS inhibits TR */
|
|
ta_cs &= ~TACS_TR;
|
|
break;
|
|
|
|
case 1: /* TADB */
|
|
if (PA & 1) /* ignore odd byte */
|
|
break;
|
|
ta_odb = data; /* return byte */
|
|
ta_cs &= ~TACS_TR; /* clear tra req */
|
|
break;
|
|
} /* end switch */
|
|
|
|
ta_updsta (NULL); /* update status */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Start a new operation - cassette is not busy */
|
|
|
|
void ta_go (void)
|
|
{
|
|
UNIT *uptr = ta_dev.units + GET_UNIT (ta_cs);
|
|
uint32 fnc = GET_FNC (ta_cs);
|
|
uint32 flg = ta_fnc_tab[fnc];
|
|
uint32 old_ust = uptr->UST;
|
|
|
|
if (DEBUG_PRS (ta_dev)) fprintf (sim_deb,
|
|
">>TA start: op=%o, old_sta = %o, pos=%d\n",
|
|
fnc, uptr->UST, uptr->pos);
|
|
ta_cs &= ~(TACS_XFRERR|TACS_EMP|TACS_TR|TACS_RDY); /* clr err, tr, rdy */
|
|
ta_bptr = 0; /* init buffer */
|
|
ta_blnt = 0;
|
|
if ((uptr->flags & UNIT_ATT) == 0) {
|
|
ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY;
|
|
return;
|
|
}
|
|
if (flg & OP_WRI) { /* write op? */
|
|
if (sim_tape_wrp (uptr)) { /* locked? */
|
|
ta_cs |= TACS_ERR|TACS_WLK|TACS_RDY; /* don't start */
|
|
return;
|
|
}
|
|
ta_odb = 0;
|
|
ta_write = 1;
|
|
}
|
|
else {
|
|
ta_idb = 0;
|
|
ta_write = 0;
|
|
}
|
|
ta_cs &= ~TACS_BEOT; /* tape in motion */
|
|
uptr->FNC = fnc; /* save function */
|
|
if ((fnc != TACS_REW) && !(flg & OP_WRI)) { /* spc/read cmd? */
|
|
t_mtrlnt t;
|
|
t_stat st;
|
|
uptr->UST = flg & UST_REV; /* save direction */
|
|
if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */
|
|
st = sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR); /* skip file gap */
|
|
if (st != MTSE_TMK) /* not there? */
|
|
sim_tape_rewind (uptr); /* restore tap pos */
|
|
else old_ust = 0; /* defang next */
|
|
}
|
|
if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* reverse in gap? */
|
|
if (uptr->UST) /* skip file gap */
|
|
sim_tape_rdrecr (uptr, ta_xb, &t, TA_MAXFR);
|
|
else sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR);
|
|
if (DEBUG_PRS (ta_dev))
|
|
fprintf (sim_deb, ">>TA skip gap: op=%o, old_sta = %o, pos=%d\n",
|
|
fnc, uptr->UST, uptr->pos);
|
|
}
|
|
}
|
|
else uptr->UST = 0;
|
|
sim_activate (uptr, ta_stime); /* schedule op */
|
|
return;
|
|
}
|
|
|
|
/* Unit service */
|
|
|
|
t_stat ta_svc (UNIT *uptr)
|
|
{
|
|
uint32 i, crc;
|
|
uint32 flg = ta_fnc_tab[uptr->FNC & TACS_M_FNC];
|
|
t_mtrlnt tbc;
|
|
t_stat st, r;
|
|
|
|
if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
|
|
ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY;
|
|
ta_updsta (uptr); /* update status */
|
|
return (ta_stopioe? SCPE_UNATT: SCPE_OK);
|
|
}
|
|
if (((flg & OP_FWD) && sim_tape_eot (uptr)) || /* illegal motion? */
|
|
((flg & OP_REV) && sim_tape_bot (uptr))) {
|
|
ta_cs |= TACS_ERR|TACS_BEOT|TACS_RDY; /* error */
|
|
ta_updsta (uptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
r = SCPE_OK;
|
|
switch (uptr->FNC) { /* case on function */
|
|
|
|
case TACS_READ: /* read start */
|
|
st = sim_tape_rdrecf (uptr, ta_xb, &ta_blnt, TA_MAXFR); /* get rec */
|
|
if (st == MTSE_RECE) /* rec in err? */
|
|
ta_cs |= TACS_ERR|TACS_CRC;
|
|
else if (st != MTSE_OK) { /* other error? */
|
|
r = ta_map_err (uptr, st); /* map error */
|
|
break;
|
|
}
|
|
crc = ta_crc (ta_xb, ta_blnt); /* calculate CRC */
|
|
ta_xb[ta_blnt++] = (crc >> 8) & 0377; /* append to buffer */
|
|
ta_xb[ta_blnt++] = crc & 0377;
|
|
uptr->FNC |= TACS_2ND; /* next state */
|
|
sim_activate (uptr, ta_ctime); /* sched next char */
|
|
return SCPE_OK;
|
|
|
|
case TACS_READ|TACS_2ND: /* read char */
|
|
if (ta_bptr < ta_blnt) /* more chars? */
|
|
ta_idb = ta_xb[ta_bptr++];
|
|
else { /* no */
|
|
ta_idb = 0;
|
|
ta_cs |= TACS_ERR|TACS_CRC; /* overrun */
|
|
break; /* tape stops */
|
|
}
|
|
if (ta_cs & TACS_ILBS) { /* CRC seq? */
|
|
uptr->FNC |= TACS_3RD; /* next state */
|
|
sim_activate (uptr, ta_stime); /* sched CRC chk */
|
|
}
|
|
else {
|
|
ta_set_tr (); /* set tra req */
|
|
sim_activate (uptr, ta_ctime); /* sched next char */
|
|
}
|
|
return SCPE_OK;
|
|
|
|
case TACS_READ|TACS_3RD: /* second read CRC */
|
|
if (ta_bptr != ta_blnt) { /* partial read? */
|
|
crc = ta_crc (ta_xb, ta_bptr + 2); /* actual CRC */
|
|
if (crc != 0) /* must be zero */
|
|
ta_cs |= TACS_ERR|TACS_CRC;
|
|
}
|
|
break; /* read done */
|
|
|
|
case TACS_WRITE: /* write start */
|
|
for (i = 0; i < TA_MAXFR; i++) /* clear buffer */
|
|
ta_xb[i] = 0;
|
|
ta_set_tr (); /* set tra req */
|
|
uptr->FNC |= TACS_2ND; /* next state */
|
|
sim_activate (uptr, ta_ctime); /* sched next char */
|
|
return SCPE_OK;
|
|
|
|
case TACS_WRITE|TACS_2ND: /* write char */
|
|
if (ta_cs & TACS_ILBS) { /* CRC seq? */
|
|
uptr->FNC |= TACS_3RD; /* next state */
|
|
sim_activate (uptr, ta_stime); /* sched wri done */
|
|
}
|
|
else {
|
|
if ((ta_bptr < TA_MAXFR) && /* room in buf? */
|
|
((uptr->pos + ta_bptr) < uptr->capac)) /* room on tape? */
|
|
ta_xb[ta_bptr++] = ta_odb; /* store char */
|
|
ta_set_tr (); /* set tra req */
|
|
sim_activate (uptr, ta_ctime); /* sched next char */
|
|
}
|
|
return SCPE_OK;
|
|
|
|
case TACS_WRITE|TACS_3RD: /* write CRC */
|
|
if (ta_bptr) { /* anything to write? */
|
|
if (st = sim_tape_wrrecf (uptr, ta_xb, ta_bptr)) /* write, err? */
|
|
r = ta_map_err (uptr, st); /* map error */
|
|
}
|
|
break; /* op done */
|
|
|
|
case TACS_WFG: /* write file gap */
|
|
if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */
|
|
r = ta_map_err (uptr, st); /* map error */
|
|
break;
|
|
|
|
case TACS_REW: /* rewind */
|
|
sim_tape_rewind (uptr);
|
|
ta_cs |= TACS_BEOT; /* bot, no error */
|
|
break;
|
|
|
|
case TACS_SRB: /* space rev blk */
|
|
if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */
|
|
r = ta_map_err (uptr, st); /* map error */
|
|
break;
|
|
|
|
case TACS_SRF: /* space rev file */
|
|
while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;
|
|
if (st == MTSE_TMK) /* if tape mark, */
|
|
ta_cs |= TACS_EOF; /* set EOF, no err */
|
|
else r = ta_map_err (uptr, st); /* else map error */
|
|
break;
|
|
|
|
case TACS_SFB: /* space fwd blk */
|
|
if (st = sim_tape_sprecf (uptr, &tbc)) /* space rev, err? */
|
|
r = ta_map_err (uptr, st); /* map error */
|
|
ta_cs |= TACS_CRC; /* CRC sets, no err */
|
|
break;
|
|
|
|
case TACS_SFF: /* space fwd file */
|
|
while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;
|
|
if (st == MTSE_TMK) /* if tape mark, */
|
|
ta_cs |= TACS_EOF; /* set EOF, no err */
|
|
else r = ta_map_err (uptr, st); /* else map error */
|
|
break;
|
|
|
|
default: /* never get here! */
|
|
return SCPE_IERR;
|
|
} /* end case */
|
|
|
|
ta_cs |= TACS_RDY; /* set ready */
|
|
ta_updsta (uptr); /* update status */
|
|
if (DEBUG_PRS (ta_dev))
|
|
fprintf (sim_deb, ">>TA done: op=%o, status = %o, pos=%d\n",
|
|
uptr->FNC, ta_cs, uptr->pos);
|
|
return r;
|
|
}
|
|
|
|
/* Update controller status */
|
|
|
|
uint32 ta_updsta (UNIT *uptr)
|
|
{
|
|
if (uptr == NULL) { /* unit specified? */
|
|
if ((uptr = ta_busy ()) == NULL) /* use busy */
|
|
uptr = ta_dev.units + GET_UNIT (ta_cs); /* use sel unit */
|
|
}
|
|
else if (ta_cs & TACS_EOF) /* save EOF */
|
|
uptr->UST |= UST_GAP;
|
|
if (uptr->flags & UNIT_ATT) /* attached? */
|
|
ta_cs &= ~TACS_EMP;
|
|
else ta_cs |= TACS_EMP|TACS_RDY; /* no, empty, ready */
|
|
if ((ta_cs & TACS_IE) && /* int enabled? */
|
|
(ta_cs & (TACS_TR|TACS_RDY))) /* req or ready? */
|
|
SET_INT (TA); /* set int req */
|
|
else CLR_INT (TA); /* no, clr int req */
|
|
return ta_cs;
|
|
}
|
|
|
|
/* Set transfer request */
|
|
|
|
void ta_set_tr (void)
|
|
{
|
|
if (ta_cs & TACS_TR) /* flag still set? */
|
|
ta_cs |= (TACS_ERR|TACS_TIM);
|
|
else ta_cs |= TACS_TR; /* set xfr req */
|
|
if (ta_cs & TACS_IE) /* if ie, int req */
|
|
SET_INT (TA);
|
|
return;
|
|
}
|
|
|
|
/* Test if controller busy */
|
|
|
|
UNIT *ta_busy (void)
|
|
{
|
|
uint32 u;
|
|
UNIT *uptr;
|
|
|
|
for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */
|
|
uptr = ta_dev.units + u;
|
|
if (sim_is_active (uptr))
|
|
return uptr;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Calculate CRC on buffer */
|
|
|
|
uint32 ta_crc (uint8 *buf, uint32 cnt)
|
|
{
|
|
uint32 crc, i, j;
|
|
|
|
crc = 0;
|
|
for (i = 0; i < cnt; i++) {
|
|
crc = crc ^ (((uint32) buf[i]) << 8);
|
|
for (j = 0; j < 8; j++) {
|
|
if (crc & 1)
|
|
crc = (crc >> 1) ^ 0xA001;
|
|
else crc = crc >> 1;
|
|
}
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
/* Map error status */
|
|
|
|
t_stat ta_map_err (UNIT *uptr, t_stat st)
|
|
{
|
|
switch (st) {
|
|
|
|
case MTSE_FMT: /* illegal fmt */
|
|
case MTSE_UNATT: /* unattached */
|
|
ta_cs |= TACS_ERR|TACS_CRC;
|
|
case MTSE_OK: /* no error */
|
|
return SCPE_IERR; /* never get here! */
|
|
|
|
case MTSE_TMK: /* end of file */
|
|
ta_cs |= TACS_ERR|TACS_EOF;
|
|
break;
|
|
|
|
case MTSE_IOERR: /* IO error */
|
|
ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */
|
|
if (ta_stopioe)
|
|
return SCPE_IOERR;
|
|
break;
|
|
|
|
case MTSE_INVRL: /* invalid rec lnt */
|
|
ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */
|
|
return SCPE_MTRLNT;
|
|
|
|
case MTSE_RECE: /* record in error */
|
|
case MTSE_EOM: /* end of medium */
|
|
ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */
|
|
break;
|
|
|
|
case MTSE_BOT: /* reverse into BOT */
|
|
ta_cs |= TACS_ERR|TACS_BEOT; /* set bot */
|
|
break;
|
|
|
|
case MTSE_WRP: /* write protect */
|
|
ta_cs |= TACS_ERR|TACS_WLK; /* set wlk err */
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat ta_reset (DEVICE *dptr)
|
|
{
|
|
uint32 u;
|
|
UNIT *uptr;
|
|
|
|
ta_cs = 0;
|
|
ta_idb = 0;
|
|
ta_odb = 0;
|
|
ta_write = 0;
|
|
ta_bptr = 0;
|
|
ta_blnt = 0;
|
|
CLR_INT (TA); /* clear interrupt */
|
|
for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */
|
|
uptr = ta_dev.units + u;
|
|
sim_cancel (uptr); /* cancel activity */
|
|
sim_tape_reset (uptr); /* reset tape */
|
|
}
|
|
if (ta_xb == NULL)
|
|
ta_xb = (uint8 *) calloc (TA_MAXFR + 2, sizeof (uint8));
|
|
if (ta_xb == NULL)
|
|
return SCPE_MEM;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Attach routine */
|
|
|
|
t_stat ta_attach (UNIT *uptr, char *cptr)
|
|
{
|
|
t_stat r;
|
|
|
|
r = sim_tape_attach (uptr, cptr);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
ta_updsta (NULL);
|
|
uptr->UST = 0;
|
|
return r;
|
|
}
|
|
|
|
/* Detach routine */
|
|
|
|
t_stat ta_detach (UNIT* uptr)
|
|
{
|
|
t_stat r;
|
|
|
|
if (!(uptr->flags & UNIT_ATT)) /* check attached */
|
|
return SCPE_OK;
|
|
r = sim_tape_detach (uptr);
|
|
ta_updsta (NULL);
|
|
uptr->UST = 0;
|
|
return r;
|
|
}
|