2473 lines
106 KiB
C
2473 lines
106 KiB
C
/* pdp11_tq.c: TMSCP tape controller simulator
|
|
|
|
Copyright (c) 2002-2013, 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 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.
|
|
|
|
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.
|
|
|
|
tq TQK50 tape controller
|
|
|
|
23-Oct-13 RMS Revised for new boot setup routine
|
|
23-Jan-12 MP Added missing support for Logical EOT detection while
|
|
positioning.
|
|
17-Aug-11 RMS Added CAPACITY modifier
|
|
05-Mar-11 MP Added missing state for proper save/restore
|
|
01-Mar-11 MP - Migrated complex physical tape activities to sim_tape
|
|
- adopted use of asynch I/O interfaces from sim_tape
|
|
- Added differing detailed debug output via sim_debug
|
|
14-Jan-11 MP Various fixes discovered while exploring Ultrix issue:
|
|
- Set UNIT_SXC flag when a tape mark is encountered
|
|
during forward motion read operations.
|
|
- Fixed logic which clears UNIT_SXC to check command
|
|
modifier.
|
|
- Added CMF_WR flag to tq_cmf entry for OP_WTM.
|
|
- Made Non-immediate rewind positioning operations
|
|
take 2 seconds.
|
|
- Added UNIT_IDLE flag to tq units.
|
|
- Fixed debug output of tape file positions when they
|
|
are 64b. Added more debug output after positioning
|
|
operations. Also, added textual display of the
|
|
command being performed (GUS,POS,RD,WR,etc@)
|
|
18-Jun-07 RMS Added UNIT_IDLE flag to timer thread
|
|
16-Feb-06 RMS Revised for new magtape capacity checking
|
|
31-Oct-05 RMS Fixed address width for large files
|
|
16-Aug-05 RMS Fixed C++ declaration and cast problems
|
|
22-Jul-05 RMS Fixed warning from Solaris C (Doug Gwyn)
|
|
30-Sep-04 RMS Revised Unibus interface
|
|
12-Jun-04 RMS Fixed bug in reporting write protect (Lyle Bickley)
|
|
18-Apr-04 RMS Fixed TQK70 media ID and model byte (Robert Schaffrath)
|
|
26-Mar-04 RMS Fixed warnings with -std=c99
|
|
25-Jan-04 RMS Revised for device debug support
|
|
19-May-03 RMS Revised for new conditional compilation scheme
|
|
25-Apr-03 RMS Revised for extended file support
|
|
28-Mar-03 RMS Added multiformat support
|
|
28-Feb-03 RMS Added variable controller, user-defined drive support
|
|
26-Feb-03 RMS Fixed bug in vector calculation for VAXen
|
|
22-Feb-03 RMS Fixed ordering bug in queue process
|
|
Fixed flags table to allow MD_CSE everywhere
|
|
09-Jan-03 RMS Fixed bug in transfer end packet status
|
|
17-Oct-02 RMS Fixed bug in read reverse (Hans Pufal)
|
|
*/
|
|
|
|
#if defined (VM_PDP10) /* PDP10 version */
|
|
#error "TQK50 not supported on PDP-10!"
|
|
|
|
#elif defined (VM_VAX) /* VAX version */
|
|
#include "vax_defs.h"
|
|
#if (UNIBUS)
|
|
#define INIT_TYPE TQ8_TYPE
|
|
#define INIT_CAP TQ8_CAP
|
|
#else
|
|
#define INIT_TYPE TQ5_TYPE
|
|
#define INIT_CAP TQ5_CAP
|
|
#endif
|
|
|
|
#else /* PDP-11 version */
|
|
#include "pdp11_defs.h"
|
|
#define INIT_TYPE TQ5_TYPE
|
|
#define INIT_CAP TQ5_CAP
|
|
#endif
|
|
|
|
#include "pdp11_uqssp.h"
|
|
#include "pdp11_mscp.h"
|
|
#include "sim_tape.h"
|
|
|
|
#define UF_MSK (UF_SCH|UF_VSS|UF_CMR|UF_CMW) /* settable flags */
|
|
|
|
#define TQ_SH_MAX 24 /* max display wds */
|
|
#define TQ_SH_PPL 8 /* wds per line */
|
|
#define TQ_SH_DPL 4 /* desc per line */
|
|
#define TQ_SH_RI 001 /* show rings */
|
|
#define TQ_SH_FR 002 /* show free q */
|
|
#define TQ_SH_RS 004 /* show resp q */
|
|
#define TQ_SH_UN 010 /* show unit q's */
|
|
#define TQ_SH_ALL 017 /* show all */
|
|
|
|
#define TQ_CLASS 1 /* TQK50 class */
|
|
#define TQ_DHTMO 0 /* def host timeout */
|
|
#define TQ_DCTMO 120 /* def ctrl timeout */
|
|
#define TQ_NUMDR 4 /* # drives */
|
|
#define TQ_MAXFR (1 << 16) /* max xfer */
|
|
|
|
#define UNIT_V_ONL (MTUF_V_UF + 0) /* online */
|
|
#define UNIT_V_ATP (MTUF_V_UF + 1) /* attn pending */
|
|
#define UNIT_V_SXC (MTUF_V_UF + 2) /* serious exc */
|
|
#define UNIT_V_POL (MTUF_V_UF + 3) /* position lost */
|
|
#define UNIT_V_TMK (MTUF_V_UF + 4) /* tape mark seen */
|
|
#define UNIT_ONL (1 << UNIT_V_ONL)
|
|
#define UNIT_ATP (1 << UNIT_V_ATP)
|
|
#define UNIT_SXC (1 << UNIT_V_SXC)
|
|
#define UNIT_POL (1 << UNIT_V_POL)
|
|
#define UNIT_TMK (1 << UNIT_V_TMK)
|
|
#define cpkt us9 /* current packet */
|
|
#define pktq us10 /* packet queue */
|
|
#define uf buf /* settable unit flags */
|
|
#define objp wait /* object position */
|
|
#define unit_plug u4 /* drive unit plug value */
|
|
#define io_status u5 /* io status from callback */
|
|
#define io_complete u6 /* io completion flag */
|
|
#define TQ_WPH(u) ((sim_tape_wrp (u))? UF_WPH: 0)
|
|
#define results up7 /* xfer buffer & results */
|
|
|
|
#define CST_S1 0 /* init stage 1 */
|
|
#define CST_S1_WR 1 /* stage 1 wrap */
|
|
#define CST_S2 2 /* init stage 2 */
|
|
#define CST_S3 3 /* init stage 3 */
|
|
#define CST_S3_PPA 4 /* stage 3 sa wait */
|
|
#define CST_S3_PPB 5 /* stage 3 ip wait */
|
|
#define CST_S4 6 /* stage 4 */
|
|
#define CST_UP 7 /* online */
|
|
#define CST_DEAD 8 /* fatal error */
|
|
|
|
#define tq_comm tq_rq.ba
|
|
|
|
#define ERR 0 /* must be SCPE_OK! */
|
|
#define OK 1
|
|
|
|
#define CMF_IMM 0x10000 /* immediate */
|
|
#define CMF_SEQ 0x20000 /* sequential */
|
|
#define CMF_WR 0x40000 /* write */
|
|
#define CMF_RW 0x80000 /* resp to GCS */
|
|
|
|
/* Internal packet management */
|
|
|
|
#define TQ_NPKTS 32 /* # packets (pwr of 2) */
|
|
#define TQ_M_NPKTS (TQ_NPKTS - 1) /* mask */
|
|
#define TQ_PKT_SIZE_W 32 /* payload size (wds) */
|
|
#define TQ_PKT_SIZE (TQ_PKT_SIZE_W * sizeof (int16))
|
|
|
|
struct tqpkt {
|
|
int16 link; /* link to next */
|
|
uint16 d[TQ_PKT_SIZE_W]; /* data */
|
|
};
|
|
|
|
/* Packet payload extraction and insertion */
|
|
|
|
#define GETP(p,w,f) ((tq_pkt[p].d[w] >> w##_V_##f) & w##_M_##f)
|
|
#define GETP32(p,w) (((uint32) tq_pkt[p].d[w]) | \
|
|
(((uint32) tq_pkt[p].d[(w)+1]) << 16))
|
|
#define PUTP32(p,w,x) tq_pkt[p].d[w] = (x) & 0xFFFF; \
|
|
tq_pkt[p].d[(w)+1] = ((x) >> 16) & 0xFFFF
|
|
|
|
/* Controller and device types - TQK50 must be swre rev 5 or later */
|
|
|
|
#define TQ5_TYPE 0 /* TK50 */
|
|
#define TQ5_UQPM 3 /* UQ port ID */
|
|
#define TQ5_CMOD 9 /* ctrl ID */
|
|
#define TQ5_UMOD 3 /* unit ID */
|
|
#define TQ5_MED 0x6D68B032 /* media ID */
|
|
#define TQ5_CREV ((1 << 8) | 5) /* ctrl revs */
|
|
#define TQ5_FREV 0 /* formatter revs */
|
|
#define TQ5_UREV 0 /* unit revs */
|
|
#define TQ5_CAP (94 * (1 << 20)) /* capacity */
|
|
#define TQ5_FMT (TF_CTP|TF_CTP_LO) /* menu */
|
|
|
|
#define TQ7_TYPE 1 /* TK70 */
|
|
#define TQ7_UQPM 14 /* UQ port ID */
|
|
#define TQ7_CMOD 14 /* ctrl ID */
|
|
#define TQ7_UMOD 14 /* unit ID */
|
|
#define TQ7_MED 0x6D68B046 /* media ID */
|
|
#define TQ7_CREV ((1 << 8) | 5) /* ctrl revs */
|
|
#define TQ7_FREV 0 /* formatter revs */
|
|
#define TQ7_UREV 0 /* unit revs */
|
|
#define TQ7_CAP (300 * (1 << 20)) /* capacity */
|
|
#define TQ7_FMT (TF_CTP|TF_CTP_LO) /* menu */
|
|
|
|
#define TQ8_TYPE 2 /* TU81 */
|
|
#define TQ8_UQPM 5 /* UQ port ID */
|
|
#define TQ8_CMOD 5 /* ctrl ID */
|
|
#define TQ8_UMOD 2 /* unit ID */
|
|
#define TQ8_MED 0x6D695051 /* media ID */
|
|
#define TQ8_CREV ((1 << 8) | 5) /* ctrl revs */
|
|
#define TQ8_FREV 0 /* formatter revs */
|
|
#define TQ8_UREV 0 /* unit revs */
|
|
#define TQ8_CAP (180 * (1 << 20)) /* capacity */
|
|
#define TQ8_FMT (TF_9TK|TF_9TK_GRP) /* menu */
|
|
|
|
#define TQU_TYPE 3 /* TKuser defined */
|
|
#define TQU_UQPM 3 /* UQ port ID */
|
|
#define TQU_CMOD 9 /* ctrl ID */
|
|
#define TQU_UMOD 3 /* unit ID */
|
|
#define TQU_MED 0x6D68B032 /* media ID */
|
|
#define TQU_CREV ((1 << 8) | 5) /* ctrl revs */
|
|
#define TQU_FREV 0 /* formatter revs */
|
|
#define TQU_UREV 0 /* unit revs */
|
|
#define TQU_CAP (94 * (1 << 20)) /* capacity */
|
|
#define TQU_FMT (TF_CTP|TF_CTP_LO) /* menu */
|
|
#define TQU_MINC 30 /* min cap MB */
|
|
#define TQU_MAXC 2000 /* max cap MB */
|
|
#define TQU_EMAXC 2000000000 /* ext max cap MB */
|
|
|
|
#define TQ_DRV(d) \
|
|
d##_UQPM, \
|
|
d##_CMOD, d##_MED, d##_FMT, d##_CAP, \
|
|
d##_UMOD, d##_CREV, d##_FREV, d##_UREV
|
|
|
|
#define TEST_EOT(u) (sim_tape_eot (u))
|
|
|
|
struct drvtyp {
|
|
uint32 uqpm; /* UQ port model */
|
|
uint16 cmod; /* ctrl model */
|
|
uint32 med; /* MSCP media */
|
|
uint16 fmt; /* flags */
|
|
t_addr cap; /* capacity */
|
|
uint16 umod; /* unit model */
|
|
uint16 cver;
|
|
uint16 fver;
|
|
uint16 uver;
|
|
const char *name;
|
|
};
|
|
|
|
static struct drvtyp drv_tab[] = {
|
|
{ TQ_DRV (TQ5), "TK50" },
|
|
{ TQ_DRV (TQ7), "TK70" },
|
|
{ TQ_DRV (TQ8), "TU81" },
|
|
{ TQ_DRV (TQU), "TKUSER" },
|
|
};
|
|
|
|
/* Data */
|
|
|
|
uint32 tq_sa = 0; /* status, addr */
|
|
uint32 tq_saw = 0; /* written data */
|
|
uint32 tq_s1dat = 0; /* S1 data */
|
|
uint32 tq_csta = 0; /* ctrl state */
|
|
uint32 tq_perr = 0; /* last error */
|
|
uint16 tq_cflgs = 0; /* ctrl flags */
|
|
uint32 tq_prgi = 0; /* purge int */
|
|
uint32 tq_pip = 0; /* poll in progress */
|
|
struct uq_ring tq_cq = { 0 }; /* cmd ring */
|
|
struct uq_ring tq_rq = { 0 }; /* rsp ring */
|
|
struct tqpkt tq_pkt[TQ_NPKTS]; /* packet queue */
|
|
uint16 tq_freq = 0; /* free list */
|
|
uint16 tq_rspq = 0; /* resp list */
|
|
uint16 tq_max_plug; /* highest unit plug number */
|
|
uint32 tq_pbsy = 0; /* #busy pkts */
|
|
uint32 tq_credits = 0; /* credits */
|
|
uint32 tq_hat = 0; /* host timer */
|
|
uint32 tq_htmo = TQ_DHTMO; /* host timeout */
|
|
int32 tq_itime = 200; /* init time, except */
|
|
int32 tq_itime4 = 10; /* stage 4 */
|
|
int32 tq_qtime = 200; /* queue time */
|
|
int32 tq_xtime = 500; /* transfer time */
|
|
int32 tq_rwtime = 2000000; /* rewind time 2 sec (adjusted later) */
|
|
int32 tq_typ = INIT_TYPE; /* device type */
|
|
|
|
/* Command table - legal modifiers (low 16b) and flags (high 16b) */
|
|
|
|
static uint32 tq_cmf[64] = {
|
|
0, /* 0 */
|
|
CMF_IMM, /* abort */
|
|
CMF_IMM|MD_CSE, /* get cmd status */
|
|
CMF_IMM|MD_CSE|MD_NXU, /* get unit status */
|
|
CMF_IMM|MD_CSE, /* set ctrl char */
|
|
0, 0, 0, /* 5-7 */
|
|
CMF_SEQ|MD_ACL|MD_CDL|MD_CSE|MD_EXA|MD_UNL, /* available */
|
|
CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* online */
|
|
CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* set unit char */
|
|
CMF_IMM, /* define acc paths */
|
|
0, 0, 0, 0, /* 12-15 */
|
|
CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* access */
|
|
MD_SCH|MD_SEC|MD_SER,
|
|
0, /* 17 */
|
|
CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase */
|
|
CMF_SEQ|CMF_WR|MD_CDL|MD_CSE, /* flush */
|
|
0, 0, /* 20-21 */
|
|
CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase gap */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, /* 22-31 */
|
|
CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* compare */
|
|
MD_SCH|MD_SEC|MD_SER,
|
|
CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV|MD_CMP| /* read */
|
|
MD_SCH|MD_SEC|MD_SER,
|
|
CMF_SEQ|CMF_RW|CMF_WR|MD_CDL|MD_CSE|MD_IMM| /* write */
|
|
MD_CMP|MD_ERW|MD_SEC|MD_SER,
|
|
0, /* 35 */
|
|
CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* wr tape mark */
|
|
CMF_SEQ|MD_CDL|MD_CSE|MD_IMM|MD_OBC| /* reposition */
|
|
MD_REV|MD_RWD|MD_DLE|
|
|
MD_SCH|MD_SEC|MD_SER,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 38-47 */
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
static const char *tq_cmdname[] = {
|
|
"", /* 0 */
|
|
"ABO", /* 1 b: abort */
|
|
"GCS", /* 2 b: get command status */
|
|
"GUS", /* 3 b: get unit status */
|
|
"SCC", /* 4 b: set controller char */
|
|
"","","", /* 5-7 */
|
|
"AVL", /* 8 b: available */
|
|
"ONL", /* 9 b: online */
|
|
"SUC", /* 10 b: set unit char */
|
|
"DAP", /* 11 b: det acc paths - nop */
|
|
"","","","", /* 12-15 */
|
|
"ACC", /* 16 b: access */
|
|
"CCD", /* 17 d: compare - nop */
|
|
"ERS", /* 18 b: erase */
|
|
"FLU", /* 19 d: flush - nop */
|
|
"","", /* 20-21 */
|
|
"ERG", /* 22 t: erase gap */
|
|
"","","","","","","","","", /* 23-31 */
|
|
"CMP", /* 32 b: compare */
|
|
"RD", /* 33 b: read */
|
|
"WR", /* 34 b: write */
|
|
"", /* 35 */
|
|
"WTM", /* 36 t: write tape mark */
|
|
"POS", /* 37 t: reposition */
|
|
"","","","","","","","","", /* 38-46 */
|
|
"FMT", /* 47 d: format */
|
|
"","","","","","","","","","","","","","","","", /* 48-63 */
|
|
"AVA", /* 64 b: unit now avail */
|
|
};
|
|
|
|
/* Forward references */
|
|
|
|
t_stat tq_rd (int32 *data, int32 PA, int32 access);
|
|
t_stat tq_wr (int32 data, int32 PA, int32 access);
|
|
int32 tq_inta (void);
|
|
t_stat tq_svc (UNIT *uptr);
|
|
t_stat tq_tmrsvc (UNIT *uptr);
|
|
t_stat tq_quesvc (UNIT *uptr);
|
|
t_stat tq_reset (DEVICE *dptr);
|
|
t_stat tq_attach (UNIT *uptr, CONST char *cptr);
|
|
t_stat tq_detach (UNIT *uptr);
|
|
t_stat tq_boot (int32 unitno, DEVICE *dptr);
|
|
t_stat tq_show_ctrl (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat tq_show_unitq (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat tq_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat tq_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat tq_set_plug (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat tq_show_plug (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat tq_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
|
const char *tq_description (DEVICE *dptr);
|
|
|
|
t_bool tq_step4 (void);
|
|
t_bool tq_mscp (uint16 pkt, t_bool q);
|
|
t_bool tq_abo (uint16 pkt);
|
|
t_bool tq_avl (uint16 pkt);
|
|
t_bool tq_erase (uint16 pkt);
|
|
t_bool tq_flu (uint16 pkt);
|
|
t_bool tq_gcs (uint16 pkt);
|
|
t_bool tq_gus (uint16 pkt);
|
|
t_bool tq_onl (uint16 pkt);
|
|
t_bool tq_pos (uint16 pkt);
|
|
t_bool tq_rw (uint16 pkt);
|
|
t_bool tq_scc (uint16 pkt);
|
|
t_bool tq_suc (uint16 pkt);
|
|
t_bool tq_wtm (uint16 pkt);
|
|
t_bool tq_plf (uint32 err);
|
|
t_bool tq_dte (UNIT *uptr, uint16 err);
|
|
t_bool tq_hbe (UNIT *uptr, uint32 ba);
|
|
t_bool tq_una (UNIT *uptr);
|
|
uint32 tq_map_status (UNIT *uptr, t_stat st);
|
|
void tq_rdbuff_top (UNIT *uptr, t_mtrlnt *tbc);
|
|
uint32 tq_rdbuff_bottom (UNIT *uptr, t_mtrlnt *tbc);
|
|
void tq_rdbufr_top (UNIT *uptr, t_mtrlnt *tbc);
|
|
uint32 tq_rdbufr_bottom (UNIT *uptr, t_mtrlnt *tbc);
|
|
t_bool tq_deqf (uint16 *pkt);
|
|
uint16 tq_deqh (uint16 *lh);
|
|
void tq_enqh (uint16 *lh, int16 pkt);
|
|
void tq_enqt (uint16 *lh, int16 pkt);
|
|
t_bool tq_getpkt (uint16 *pkt);
|
|
t_bool tq_putpkt (uint16 pkt, t_bool qt);
|
|
t_bool tq_getdesc (struct uq_ring *ring, uint32 *desc);
|
|
t_bool tq_putdesc (struct uq_ring *ring, uint32 desc);
|
|
uint16 tq_mot_valid (UNIT *uptr, uint32 cmd);
|
|
t_stat tq_mot_err (UNIT *uptr, uint32 rsiz);
|
|
t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint16 sts, uint32 rsiz);
|
|
void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint16 sts, uint16 lnt, uint16 typ);
|
|
void tq_putr_unit (int16 pkt, UNIT *uptr, uint16 lu, t_bool all);
|
|
void tq_setf_unit (int16 pkt, UNIT *uptr);
|
|
uint32 tq_efl (UNIT *uptr);
|
|
void tq_init_int (void);
|
|
void tq_ring_int (struct uq_ring *ring);
|
|
t_bool tq_fatal (uint32 err);
|
|
UNIT *tq_getucb (uint16 lu);
|
|
|
|
/* TQ data structures
|
|
|
|
tq_dev TQ device descriptor
|
|
tq_unit TQ unit list
|
|
tq_reg TQ register list
|
|
tq_mod TQ modifier list
|
|
*/
|
|
|
|
#define IOLN_TQ 004
|
|
|
|
DIB tq_dib = {
|
|
IOBA_AUTO, IOLN_TQ, &tq_rd, &tq_wr,
|
|
1, IVCL (TQ), 0, { &tq_inta }, IOLN_TQ,
|
|
};
|
|
|
|
UNIT tq_unit[] = {
|
|
{ UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, INIT_CAP) },
|
|
{ UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, INIT_CAP) },
|
|
{ UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, INIT_CAP) },
|
|
{ UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, INIT_CAP) },
|
|
{ UDATA (&tq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) },
|
|
{ UDATA (&tq_quesvc, UNIT_IDLE|UNIT_DIS, 0) }
|
|
};
|
|
|
|
#define TQ_TIMER (TQ_NUMDR)
|
|
#define TQ_QUEUE (TQ_TIMER + 1)
|
|
|
|
REG tq_reg[] = {
|
|
{ GRDATAD (SA, tq_sa, DEV_RDX, 16, 0, "status/address register") },
|
|
{ GRDATAD (SAW, tq_saw, DEV_RDX, 16, 0, "written data") },
|
|
{ GRDATAD (S1DAT, tq_s1dat, DEV_RDX, 16, 0, "step 1 init host data") },
|
|
{ GRDATAD (CQIOFF, tq_cq.ioff, DEV_RDX, 32, 0, "command queue intr offset") },
|
|
{ GRDATAD (CQBA, tq_cq.ba, DEV_RDX, 22, 0, "command queue base address") },
|
|
{ GRDATAD (CQLNT, tq_cq.lnt, DEV_RDX, 8, 2, "command queue length"), REG_NZ },
|
|
{ GRDATAD (CQIDX, tq_cq.idx, DEV_RDX, 8, 2, "command queue index") },
|
|
{ GRDATAD (TQIOFF, tq_rq.ioff, DEV_RDX, 32, 0, "request queue intr offset") },
|
|
{ GRDATAD (TQBA, tq_rq.ba, DEV_RDX, 22, 0, "request queue base address") },
|
|
{ GRDATAD (TQLNT, tq_rq.lnt, DEV_RDX, 8, 2, "request queue length"), REG_NZ },
|
|
{ GRDATAD (TQIDX, tq_rq.idx, DEV_RDX, 8, 2, "request queue index") },
|
|
{ DRDATAD (FREE, tq_freq, 5, "head of free packet list") },
|
|
{ DRDATAD (RESP, tq_rspq, 5, "head of response packet list") },
|
|
{ DRDATAD (PBSY, tq_pbsy, 5, "number of busy packets") },
|
|
{ GRDATAD (CFLGS, tq_cflgs, DEV_RDX, 16, 0, "controller flags") },
|
|
{ GRDATAD (CSTA, tq_csta, DEV_RDX, 4, 0, "controller state") },
|
|
{ GRDATAD (PERR, tq_perr, DEV_RDX, 9, 0, "port error number") },
|
|
{ DRDATAD (CRED, tq_credits, 5, "host credits") },
|
|
{ DRDATAD (HAT, tq_hat, 17, "host available timer") },
|
|
{ DRDATAD (HTMO, tq_htmo, 17, "host timeout value") },
|
|
{ URDATAD (CPKT, tq_unit[0].cpkt, 10, 5, 0, TQ_NUMDR, 0, "current packet, units 0 to 3") },
|
|
{ URDATAD (PKTQ, tq_unit[0].pktq, 10, 5, 0, TQ_NUMDR, 0, "packet queue, units 0 to 3") },
|
|
{ URDATAD (UFLG, tq_unit[0].uf, DEV_RDX, 16, 0, TQ_NUMDR, 0, "unit flags, units 0 to 3") },
|
|
{ URDATAD (POS, tq_unit[0].pos, 10, T_ADDR_W, 0, TQ_NUMDR, 0, "position, units 0 to 3") },
|
|
{ URDATAD (OBJP, tq_unit[0].objp, 10, 32, 0, TQ_NUMDR, 0, "object position, units 0 to 3") },
|
|
{ FLDATA (PRGI, tq_prgi, 0), REG_HIDDEN },
|
|
{ FLDATA (PIP, tq_pip, 0), REG_HIDDEN },
|
|
{ FLDATAD (INT, IREQ (TQ), INT_V_TQ, "interrupt pending flag") },
|
|
{ DRDATAD (ITIME, tq_itime, 24, "init time delay, except stage 4"), PV_LEFT + REG_NZ },
|
|
{ DRDATAD (I4TIME, tq_itime4, 24, "init stage 4 delay"), PV_LEFT + REG_NZ },
|
|
{ DRDATAD (QTIME, tq_qtime, 24, "response time for 'immediate' packets"), PV_LEFT + REG_NZ },
|
|
{ DRDATAD (XTIME, tq_xtime, 24, "response time for data transfers"), PV_LEFT + REG_NZ },
|
|
{ DRDATAD (RWTIME, tq_rwtime, 32, "rewind time 2 sec (adjusted later)"), PV_LEFT + REG_NZ },
|
|
{ BRDATAD (PKTS, tq_pkt, DEV_RDX, 16, TQ_NPKTS * (TQ_PKT_SIZE_W + 1), "packet buffers, 33W each, 32 entries") },
|
|
{ URDATAD (PLUG, tq_unit[0].unit_plug, 10, T_ADDR_W, 0, TQ_NUMDR, PV_LEFT | REG_RO, "unit plug value, units 0 to 3") },
|
|
{ DRDATA (DEVTYPE, tq_typ, 2), REG_HRO },
|
|
{ DRDATA (DEVCAP, drv_tab[TQU_TYPE].cap, T_ADDR_W), PV_LEFT | REG_HRO },
|
|
{ GRDATA (DEVADDR, tq_dib.ba, DEV_RDX, 32, 0), REG_HRO },
|
|
{ GRDATA (DEVVEC, tq_dib.vec, DEV_RDX, 16, 0), REG_HRO },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB tq_mod[] = {
|
|
{ MTUF_WLK, 0, "write enabled", "WRITEENABLED",
|
|
NULL, NULL, NULL, "Write enable tape drive" },
|
|
{ MTUF_WLK, MTUF_WLK, "write locked", "LOCKED",
|
|
NULL, NULL, NULL, "Write lock tape drive" },
|
|
{ MTAB_XTD|MTAB_VDV, TQ5_TYPE, NULL, "TK50",
|
|
&tq_set_type, NULL, NULL, "Set TK50 Device Type" },
|
|
{ MTAB_XTD|MTAB_VDV, TQ7_TYPE, NULL, "TK70",
|
|
&tq_set_type, NULL, NULL, "Set TK70 Device Type" },
|
|
{ MTAB_XTD|MTAB_VDV, TQ8_TYPE, NULL, "TU81",
|
|
&tq_set_type, NULL, NULL, "Set TU81 Device Type" },
|
|
{ MTAB_XTD|MTAB_VDV, TQU_TYPE, NULL, "TKUSER",
|
|
&tq_set_type, NULL, NULL, "Set TKUSER=size Device Type" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "TYPE", NULL,
|
|
NULL, &tq_show_type, NULL, "Display device type" },
|
|
{ MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "UNIT", "UNIT=val (0-65534)",
|
|
&tq_set_plug, &tq_show_plug, NULL, "Set/Display Unit plug value" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, TQ_SH_RI, "RINGS", NULL,
|
|
NULL, &tq_show_ctrl, NULL, "Display command and response rings" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, TQ_SH_FR, "FREEQ", NULL,
|
|
NULL, &tq_show_ctrl, NULL, "Display free queue" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, TQ_SH_RS, "RESPQ", NULL,
|
|
NULL, &tq_show_ctrl, NULL, "Display response queue" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, TQ_SH_UN, "UNITQ", NULL,
|
|
NULL, &tq_show_ctrl, NULL, "Display all unit queues" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, TQ_SH_ALL, "ALL", NULL,
|
|
NULL, &tq_show_ctrl, NULL, "Display complete controller state" },
|
|
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "UNITQ", NULL,
|
|
NULL, &tq_show_unitq, NULL, "Display unit queue" },
|
|
{ MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "FORMAT", "FORMAT",
|
|
&sim_tape_set_fmt, &sim_tape_show_fmt, NULL, "Set/Display tape format (SIMH, E11, TPC, P7B, AWS, TAR)" },
|
|
{ MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "CAPACITY", "CAPACITY",
|
|
&sim_tape_set_capac, &sim_tape_show_capac, NULL, "Set/Display capacity" },
|
|
#if defined (VM_PDP11)
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 004, "ADDRESS", "ADDRESS",
|
|
&set_addr, &show_addr, NULL, "Bus address" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "AUTOCONFIGURE",
|
|
&set_addr_flt, NULL, NULL, "Enable autoconfiguration of address & vector" },
|
|
#else
|
|
{ MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL,
|
|
NULL, &show_addr, NULL, "Bus address" },
|
|
#endif
|
|
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL,
|
|
NULL, &show_vec, NULL, "Interrupt vector" },
|
|
{ 0 }
|
|
};
|
|
|
|
/* debugging bitmaps */
|
|
#define DBG_TRC 0x0001 /* trace routine calls */
|
|
#define DBG_INI 0x0002 /* display setup/init sequence info */
|
|
#define DBG_REG 0x0004 /* trace read/write registers */
|
|
#define DBG_REQ 0x0008 /* display transfer requests */
|
|
#define DBG_TAP 0x0010 /* display sim_tape activities */
|
|
#define DBG_STR MTSE_DBG_STR /* display tape structure detail */
|
|
#define DBG_POS MTSE_DBG_POS /* display position activities */
|
|
#define DBG_DAT MTSE_DBG_DAT /* display transfer data */
|
|
|
|
DEBTAB tq_debug[] = {
|
|
{"TRACE", DBG_TRC, "trace routine calls"},
|
|
{"INIT", DBG_INI, "display setup/init sequence info"},
|
|
{"REG", DBG_REG, "trace read/write registers"},
|
|
{"REQ", DBG_REQ, "display transfer requests"},
|
|
{"TAPE", DBG_TAP, "display sim_tape activities"},
|
|
{"STR", DBG_STR, "display tape structure detail"},
|
|
{"POS", DBG_POS, "display position activities"},
|
|
{"DATA", DBG_DAT, "display transfer data"},
|
|
{0}
|
|
};
|
|
|
|
DEVICE tq_dev = {
|
|
"TQ", tq_unit, tq_reg, tq_mod,
|
|
TQ_NUMDR + 2, 10, T_ADDR_W, 1, DEV_RDX, 8,
|
|
NULL, NULL, &tq_reset,
|
|
&tq_boot, &tq_attach, &tq_detach,
|
|
&tq_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG | DEV_TAPE,
|
|
0, tq_debug,
|
|
NULL, NULL, &tq_help, NULL, NULL,
|
|
&tq_description
|
|
};
|
|
|
|
|
|
struct tq_req_results { /* intermediate State during tape motion commands */
|
|
t_stat io_status;
|
|
int32 io_complete;
|
|
int rewind_done;
|
|
uint32 sts;
|
|
uint32 sktmk;
|
|
uint32 skrec;
|
|
t_mtrlnt tbc;
|
|
int32 objupd;
|
|
uint8 tqxb[TQ_MAXFR];
|
|
};
|
|
|
|
/* I/O dispatch routines, I/O addresses 17774500 - 17774502
|
|
|
|
17774500 IP read/write
|
|
17774502 SA read/write
|
|
*/
|
|
|
|
t_stat tq_rd (int32 *data, int32 PA, int32 access)
|
|
{
|
|
sim_debug(DBG_REG, &tq_dev, "tq_rd(PA=0x%08X [%s], access=%d)=0x%04X\n", PA, ((PA >> 1) & 01) ? "SA" : "IP", access, ((PA >> 1) & 01) ? tq_sa : 0);
|
|
|
|
switch ((PA >> 1) & 01) { /* decode PA<1> */
|
|
case 0: /* IP */
|
|
*data = 0; /* reads zero */
|
|
if (tq_csta == CST_S3_PPB) /* waiting for poll? */
|
|
tq_step4 ();
|
|
else if (tq_csta == CST_UP) { /* if up */
|
|
tq_pip = 1; /* poll host */
|
|
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime);
|
|
}
|
|
break;
|
|
|
|
case 1: /* SA */
|
|
*data = tq_sa;
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat tq_wr (int32 data, int32 PA, int32 access)
|
|
{
|
|
sim_debug(DBG_REG, &tq_dev, "tq_wr(PA=0x%08X [%s], access=%d, data=0x%04X)\n", PA, ((PA >> 1) & 01) ? "SA" : "IP", access, data);
|
|
|
|
switch ((PA >> 1) & 01) { /* decode PA<1> */
|
|
|
|
case 0: /* IP */
|
|
tq_reset (&tq_dev); /* init device */
|
|
sim_debug (DBG_REQ, &tq_dev, "initialization started\n");
|
|
break;
|
|
|
|
case 1: /* SA */
|
|
tq_saw = data;
|
|
if (tq_csta < CST_S4) /* stages 1-3 */
|
|
sim_activate (&tq_unit[TQ_QUEUE], tq_itime);
|
|
else if (tq_csta == CST_S4) /* stage 4 (fast) */
|
|
sim_activate (&tq_unit[TQ_QUEUE], tq_itime4);
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Transition to step 4 - init communications region */
|
|
|
|
t_bool tq_step4 (void)
|
|
{
|
|
int32 i, lnt;
|
|
uint32 base;
|
|
uint16 zero[SA_COMM_MAX >> 1];
|
|
|
|
tq_rq.ioff = SA_COMM_RI; /* set intr offset */
|
|
tq_rq.ba = tq_comm; /* set rsp q base */
|
|
tq_rq.lnt = SA_S1H_RQ (tq_s1dat) << 2; /* get resp q len */
|
|
tq_cq.ioff = SA_COMM_CI; /* set intr offset */
|
|
tq_cq.ba = tq_comm + tq_rq.lnt; /* set cmd q base */
|
|
tq_cq.lnt = SA_S1H_CQ (tq_s1dat) << 2; /* get cmd q len */
|
|
tq_cq.idx = tq_rq.idx = 0; /* clear q idx's */
|
|
if (tq_prgi)
|
|
base = tq_comm + SA_COMM_QQ;
|
|
else base = tq_comm + SA_COMM_CI;
|
|
lnt = tq_comm + tq_cq.lnt + tq_rq.lnt - base; /* comm lnt */
|
|
if (lnt > SA_COMM_MAX) /* paranoia */
|
|
lnt = SA_COMM_MAX;
|
|
for (i = 0; i < (lnt >> 1); i++) /* clr buffer */
|
|
zero[i] = 0;
|
|
if (Map_WriteW (base, lnt, zero)) /* zero comm area */
|
|
return tq_fatal (PE_QWE); /* error? */
|
|
tq_sa = SA_S4 | (drv_tab[tq_typ].uqpm << SA_S4C_V_MOD) |/* send step 4 */
|
|
((drv_tab[tq_typ].cver & 0xFF) << SA_S4C_V_VER);
|
|
tq_csta = CST_S4; /* set step 4 */
|
|
tq_init_int (); /* poke host */
|
|
return OK;
|
|
}
|
|
|
|
/* Queue service - invoked when any of the queues (host queue, unit
|
|
queues, response queue) require servicing. Also invoked during
|
|
initialization to provide some delay to the next step.
|
|
|
|
Process at most one item off each unit queue
|
|
If the unit queues were empty, process at most one item off the host queue
|
|
Process at most one item off the response queue
|
|
|
|
If all queues are idle, terminate thread
|
|
*/
|
|
|
|
t_stat tq_quesvc (UNIT *uptr)
|
|
{
|
|
int32 i, cnid;
|
|
uint16 pkt = 0;
|
|
UNIT *nuptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_quesvc\n");
|
|
|
|
if (tq_csta < CST_UP) { /* still init? */
|
|
sim_debug(DBG_INI, &tq_dev, "CSTA=%d, SAW=0x%X\n", tq_csta, tq_saw);
|
|
|
|
switch (tq_csta) { /* controller state? */
|
|
case CST_S1: /* need S1 reply */
|
|
if (tq_saw & SA_S1H_VL) { /* valid? */
|
|
if (tq_saw & SA_S1H_WR) { /* wrap? */
|
|
tq_sa = tq_saw; /* echo data */
|
|
tq_csta = CST_S1_WR; /* endless loop */
|
|
}
|
|
else {
|
|
tq_s1dat = tq_saw; /* save data */
|
|
tq_dib.vec = (tq_s1dat & SA_S1H_VEC) << 2; /* get vector */
|
|
tq_sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (tq_s1dat);
|
|
tq_csta = CST_S2; /* now in step 2 */
|
|
tq_init_int (); /* intr if req */
|
|
}
|
|
} /* end if valid */
|
|
break;
|
|
|
|
case CST_S1_WR: /* wrap mode */
|
|
tq_sa = tq_saw; /* echo data */
|
|
break;
|
|
|
|
case CST_S2: /* need S2 reply */
|
|
tq_comm = tq_saw & SA_S2H_CLO; /* get low addr */
|
|
tq_prgi = tq_saw & SA_S2H_PI; /* get purge int */
|
|
tq_sa = SA_S3 | SA_S3C_EC (tq_s1dat);
|
|
tq_csta = CST_S3; /* now in step 3 */
|
|
tq_init_int (); /* intr if req */
|
|
break;
|
|
|
|
case CST_S3: /* need S3 reply */
|
|
tq_comm = ((tq_saw & SA_S3H_CHI) << 16) | tq_comm;
|
|
if (tq_saw & SA_S3H_PP) { /* purge/poll test? */
|
|
tq_sa = 0; /* put 0 */
|
|
tq_csta = CST_S3_PPA; /* wait for 0 write */
|
|
}
|
|
else tq_step4 (); /* send step 4 */
|
|
break;
|
|
|
|
case CST_S3_PPA: /* need purge test */
|
|
if (tq_saw) /* data not zero? */
|
|
tq_fatal (PE_PPF);
|
|
else tq_csta = CST_S3_PPB; /* wait for poll */
|
|
break;
|
|
|
|
case CST_S4: /* need S4 reply */
|
|
if (tq_saw & SA_S4H_GO) { /* go set? */
|
|
sim_debug (DBG_REQ, &tq_dev, "initialization complete\n");
|
|
tq_csta = CST_UP; /* we're up */
|
|
tq_sa = 0; /* clear SA */
|
|
sim_activate_after (&tq_unit[TQ_TIMER], 1000000);
|
|
if ((tq_saw & SA_S4H_LF) && tq_perr)
|
|
tq_plf (tq_perr);
|
|
tq_perr = 0;
|
|
}
|
|
break;
|
|
} /* end switch */
|
|
return SCPE_OK;
|
|
} /* end if */
|
|
|
|
for (i = 0; i < TQ_NUMDR; i++) { /* chk unit q's */
|
|
uint16 tpkt;
|
|
|
|
nuptr = tq_dev.units + i; /* ptr to unit */
|
|
if (nuptr->cpkt || (nuptr->pktq == 0))
|
|
continue;
|
|
tpkt = nuptr->pktq;
|
|
pkt = tq_deqh (&tpkt); /* get top of q */
|
|
nuptr->pktq = tpkt;
|
|
if (!tq_mscp (pkt, FALSE)) /* process */
|
|
return SCPE_OK;
|
|
}
|
|
if ((pkt == 0) && tq_pip) { /* polling? */
|
|
if (!tq_getpkt (&pkt)) /* get host pkt */
|
|
return SCPE_OK;
|
|
if (pkt) { /* got one? */
|
|
UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]);
|
|
|
|
if (up)
|
|
sim_debug (DBG_REQ, &tq_dev, "cmd=%04X(%3s), mod=%04X, unit=%d, bc=%04X%04X, ma=%04X%04X, obj=%d, pos=0x%" T_ADDR_FMT "X\n",
|
|
tq_pkt[pkt].d[CMD_OPC], tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f],
|
|
tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN],
|
|
tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL],
|
|
tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL],
|
|
up->objp, up->pos);
|
|
else
|
|
sim_debug (DBG_REQ, &tq_dev, "cmd=%04X(%3s), mod=%04X, unit=%d, bc=%04X%04X, ma=%04X%04X\n",
|
|
tq_pkt[pkt].d[CMD_OPC], tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f],
|
|
tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN],
|
|
tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL],
|
|
tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL]);
|
|
|
|
if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */
|
|
return tq_fatal (PE_PIE); /* no, term thread */
|
|
cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */
|
|
if (cnid == UQ_CID_TMSCP) { /* TMSCP packet? */
|
|
if (!tq_mscp (pkt, TRUE)) /* proc, q non-seq */
|
|
return SCPE_OK;
|
|
}
|
|
else if (cnid == UQ_CID_DUP) { /* DUP packet? */
|
|
tq_putr (pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ);
|
|
if (!tq_putpkt (pkt, TRUE)) /* ill cmd */
|
|
return SCPE_OK;
|
|
}
|
|
else return tq_fatal (PE_ICI); /* no, term thread */
|
|
} /* end if pkt */
|
|
else tq_pip = 0; /* discontinue poll */
|
|
} /* end if pip */
|
|
if (tq_rspq) { /* resp q? */
|
|
pkt = tq_deqh ((uint16 *)&tq_rspq); /* get top of q */
|
|
if (!tq_putpkt (pkt, FALSE)) /* send to host */
|
|
return SCPE_OK;
|
|
} /* end if resp q */
|
|
if (pkt) /* more to do? */
|
|
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime);
|
|
return SCPE_OK; /* done */
|
|
}
|
|
|
|
/* Clock service (roughly once per second) */
|
|
|
|
t_stat tq_tmrsvc (UNIT *uptr)
|
|
{
|
|
int32 i;
|
|
UNIT *nuptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_tmrsvc\n");
|
|
|
|
sim_activate_after (uptr, 1000000); /* reactivate */
|
|
for (i = 0; i < TQ_NUMDR; i++) { /* poll */
|
|
nuptr = tq_dev.units + i;
|
|
if ((nuptr->flags & UNIT_ATP) && /* ATN pending? */
|
|
(nuptr->flags & UNIT_ATT) && /* still online? */
|
|
(tq_cflgs & CF_ATN)) { /* wanted? */
|
|
if (!tq_una (nuptr))
|
|
return SCPE_OK;
|
|
}
|
|
nuptr->flags = nuptr->flags & ~UNIT_ATP;
|
|
}
|
|
if ((tq_hat > 0) && (--tq_hat == 0)) /* host timeout? */
|
|
tq_fatal (PE_HAT); /* fatal err */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* MSCP packet handling */
|
|
|
|
t_bool tq_mscp (uint16 pkt, t_bool q)
|
|
{
|
|
uint16 sts;
|
|
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* command */
|
|
uint32 flg = GETP (pkt, CMD_OPC, FLG); /* flags */
|
|
uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_mscp\n");
|
|
|
|
if ((cmd >= 64) || (tq_cmf[cmd] == 0)) { /* invalid cmd? */
|
|
cmd = OP_END; /* set end op */
|
|
sts = ST_CMD | I_OPCD; /* ill op */
|
|
}
|
|
else if (flg) { /* flags? */
|
|
cmd = cmd | OP_END; /* set end flag */
|
|
sts = ST_CMD | I_FLAG; /* ill flags */
|
|
}
|
|
else if (mdf & ~tq_cmf[cmd]) { /* invalid mod? */
|
|
cmd = cmd | OP_END; /* set end flag */
|
|
sts = ST_CMD | I_MODF; /* ill mods */
|
|
}
|
|
else { /* valid cmd */
|
|
if ((uptr = tq_getucb (lu))) { /* valid unit? */
|
|
if (q && (tq_cmf[cmd] & CMF_SEQ) && /* queueing, seq, */
|
|
(uptr->cpkt || uptr->pktq)) { /* and active? */
|
|
tq_enqt (&uptr->pktq, pkt); /* do later */
|
|
return OK;
|
|
}
|
|
// if (tq_cmf[cmd] & MD_CDL) /* clr cch lost? */
|
|
// uptr->flags = uptr->flags & ~UNIT_CDL;
|
|
if ((mdf & MD_CSE) && (uptr->flags & UNIT_SXC)) /* clr ser exc? */
|
|
uptr->flags = uptr->flags & ~UNIT_SXC;
|
|
memset (uptr->results, 0, sizeof (struct tq_req_results)); /* init request state */
|
|
}
|
|
switch (cmd) {
|
|
|
|
case OP_ABO: /* abort */
|
|
return tq_abo (pkt);
|
|
|
|
case OP_AVL: /* avail */
|
|
return tq_avl (pkt);
|
|
|
|
case OP_GCS: /* get cmd status */
|
|
return tq_gcs (pkt);
|
|
|
|
case OP_GUS: /* get unit status */
|
|
return tq_gus (pkt);
|
|
|
|
case OP_ONL: /* online */
|
|
return tq_onl (pkt);
|
|
|
|
case OP_SCC: /* set ctrl char */
|
|
return tq_scc (pkt);
|
|
|
|
case OP_SUC: /* set unit char */
|
|
return tq_suc (pkt);
|
|
|
|
case OP_ERS: /* erase */
|
|
case OP_ERG: /* erase gap */
|
|
return tq_erase (pkt);
|
|
|
|
case OP_FLU: /* flush */
|
|
return tq_flu (pkt);
|
|
|
|
case OP_POS: /* position */
|
|
return tq_pos (pkt);
|
|
|
|
case OP_WTM: /* write tape mark */
|
|
return tq_wtm (pkt);
|
|
|
|
case OP_ACC: /* access */
|
|
case OP_CMP: /* compare */
|
|
case OP_RD: /* read */
|
|
case OP_WR: /* write */
|
|
return tq_rw (pkt);
|
|
|
|
case OP_DAP:
|
|
cmd = cmd | OP_END; /* set end flag */
|
|
sts = ST_SUC; /* success */
|
|
break;
|
|
|
|
default:
|
|
cmd = OP_END; /* set end op */
|
|
sts = ST_CMD | I_OPCD; /* ill op */
|
|
break;
|
|
} /* end switch */
|
|
} /* end else */
|
|
tq_putr (pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Abort a command - 1st parameter is ref # of cmd to abort */
|
|
|
|
t_bool tq_abo (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */
|
|
uint16 tpkt, prv;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_abo\n");
|
|
|
|
tpkt = 0; /* set no mtch */
|
|
if ((uptr = tq_getucb (lu))) { /* get unit */
|
|
if (uptr->cpkt && /* curr pkt? */
|
|
(GETP32 (uptr->cpkt, CMD_REFL) == ref)) { /* match ref? */
|
|
tpkt = uptr->cpkt; /* save match */
|
|
uptr->cpkt = 0; /* gonzo */
|
|
sim_cancel (uptr); /* cancel unit */
|
|
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime);
|
|
}
|
|
else if (uptr->pktq && /* head of q? */
|
|
(GETP32 (uptr->pktq, CMD_REFL) == ref)) { /* match ref? */
|
|
tpkt = uptr->pktq; /* save match */
|
|
uptr->pktq = tq_pkt[tpkt].link; /* unlink */
|
|
}
|
|
else if ((prv = uptr->pktq)) { /* srch pkt q */
|
|
while ((tpkt = tq_pkt[prv].link)) { /* walk list */
|
|
if (GETP32 (tpkt, RSP_REFL) == ref) { /* match ref? */
|
|
tq_pkt[prv].link = tq_pkt[tpkt].link; /* unlink */
|
|
break;
|
|
}
|
|
prv = tpkt; /* no match, next */
|
|
}
|
|
}
|
|
if (tpkt) { /* found target? */
|
|
uint32 tcmd = GETP (tpkt, CMD_OPC, OPC); /* get opcode */
|
|
tq_putr (tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ);
|
|
if (!tq_putpkt (tpkt, TRUE))
|
|
return ERR;
|
|
}
|
|
} /* end if unit */
|
|
tq_putr (pkt, OP_ABO | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Unit available - set unit status to available
|
|
Deferred if q'd cmds, bypassed if ser exc */
|
|
|
|
t_bool tq_avl (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifiers */
|
|
uint16 sts;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_avl\n");
|
|
|
|
if ((uptr = tq_getucb (lu))) { /* unit exist? */
|
|
if (uptr->flags & UNIT_SXC) /* ser exc pending? */
|
|
sts = ST_SXC;
|
|
else {
|
|
uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_TMK | UNIT_POL);
|
|
sim_tape_rewind (uptr); /* rewind */
|
|
uptr->uf = uptr->objp = 0; /* clr flags */
|
|
if (uptr->flags & UNIT_ATT) { /* attached? */
|
|
sts = ST_SUC; /* success */
|
|
if (mdf & MD_UNL) /* unload? */
|
|
tq_detach (uptr);
|
|
}
|
|
else sts = ST_OFL | SB_OFL_NV; /* no, offline */
|
|
}
|
|
}
|
|
else sts = ST_OFL; /* offline */
|
|
tq_putr (pkt, OP_AVL | OP_END, tq_efl (uptr), sts, AVL_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Get command status - only interested in active xfr cmd */
|
|
|
|
t_bool tq_gcs (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */
|
|
int32 tpkt;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_gcs\n");
|
|
|
|
if ((uptr = tq_getucb (lu)) && /* valid lu? */
|
|
(tpkt = uptr->cpkt) && /* queued pkt? */
|
|
(GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */
|
|
(tq_cmf[GETP (tpkt, CMD_OPC, OPC)] & CMF_RW)) { /* rd/wr cmd? */
|
|
tq_pkt[pkt].d[GCS_STSL] = tq_pkt[tpkt].d[RW_BCL];
|
|
tq_pkt[pkt].d[GCS_STSH] = tq_pkt[tpkt].d[RW_BCH];
|
|
}
|
|
else tq_pkt[pkt].d[GCS_STSL] = tq_pkt[pkt].d[GCS_STSH] = 0;
|
|
tq_putr (pkt, OP_GCS | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Get unit status */
|
|
|
|
t_bool tq_gus (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint16 sts;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_gus\n");
|
|
|
|
if (tq_pkt[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */
|
|
if (lu > tq_max_plug) { /* beyond last unit plug? */
|
|
lu = 0; /* reset to 0 */
|
|
tq_pkt[pkt].d[RSP_UN] = (uint16)lu;
|
|
}
|
|
}
|
|
if ((uptr = tq_getucb (lu))) { /* unit exist? */
|
|
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
|
sts = ST_OFL | SB_OFL_NV; /* offl no vol */
|
|
else if (uptr->flags & UNIT_ONL) /* online */
|
|
sts = ST_SUC;
|
|
else sts = ST_AVL; /* avail */
|
|
tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */
|
|
tq_pkt[pkt].d[GUS_MENU] = drv_tab[tq_typ].fmt; /* format menu */
|
|
tq_pkt[pkt].d[GUS_CAP] = 0; /* free capacity */
|
|
tq_pkt[pkt].d[GUS_FVER] = drv_tab[tq_typ].fver; /* formatter version */
|
|
tq_pkt[pkt].d[GUS_UVER] = drv_tab[tq_typ].uver; /* unit version */
|
|
}
|
|
else sts = ST_OFL; /* offline */
|
|
tq_putr (pkt, OP_GUS | OP_END, tq_efl (uptr), sts, GUS_LNT_T, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Unit online - deferred if q'd commands */
|
|
|
|
t_bool tq_onl (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint16 sts;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_onl\n");
|
|
|
|
if ((uptr = tq_getucb (lu))) { /* unit exist? */
|
|
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
|
sts = ST_OFL | SB_OFL_NV; /* offl no vol */
|
|
else if (uptr->flags & UNIT_ONL) /* already online? */
|
|
sts = ST_SUC | SB_SUC_ON;
|
|
else {
|
|
sts = ST_SUC; /* mark online */
|
|
sim_tape_rewind (uptr); /* rewind */
|
|
uptr->objp = 0; /* clear flags */
|
|
uptr->flags = (uptr->flags | UNIT_ONL) &
|
|
~(UNIT_TMK | UNIT_POL); /* onl, pos ok */
|
|
tq_setf_unit (pkt, uptr); /* hack flags */
|
|
}
|
|
tq_putr_unit (pkt, uptr, lu, TRUE); /* set fields */
|
|
}
|
|
else sts = ST_OFL; /* offline */
|
|
tq_putr (pkt, OP_ONL | OP_END, tq_efl (uptr), sts, ONL_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Set controller characteristics */
|
|
|
|
t_bool tq_scc (uint16 pkt)
|
|
{
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_scc\n");
|
|
|
|
if (tq_pkt[pkt].d[SCC_MSV]) /* MSCP ver = 0? */
|
|
tq_putr (pkt, 0, 0, ST_CMD | I_VRSN, SCC_LNT, UQ_TYP_SEQ);
|
|
else {
|
|
tq_cflgs = (tq_cflgs & CF_RPL) | /* hack ctrl flgs */
|
|
tq_pkt[pkt].d[SCC_CFL];
|
|
if ((tq_htmo = tq_pkt[pkt].d[SCC_TMO])) /* set timeout */
|
|
tq_htmo = tq_htmo + 2; /* if nz, round up */
|
|
tq_pkt[pkt].d[SCC_CFL] = tq_cflgs; /* return flags */
|
|
tq_pkt[pkt].d[SCC_TMO] = TQ_DCTMO; /* ctrl timeout */
|
|
tq_pkt[pkt].d[SCC_VER] = drv_tab[tq_typ].cver; /* ctrl version */
|
|
tq_pkt[pkt].d[SCC_CIDA] = 0; /* ctrl ID */
|
|
tq_pkt[pkt].d[SCC_CIDB] = 0;
|
|
tq_pkt[pkt].d[SCC_CIDC] = 0;
|
|
tq_pkt[pkt].d[SCC_CIDD] = (TQ_CLASS << SCC_CIDD_V_CLS) |
|
|
(drv_tab[tq_typ].cmod << SCC_CIDD_V_MOD);
|
|
PUTP32 (pkt, SCC_MBCL, TQ_MAXFR); /* max bc */
|
|
tq_putr (pkt, OP_SCC | OP_END, 0, ST_SUC, SCC_LNT, UQ_TYP_SEQ);
|
|
}
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Set unit characteristics - defer if q'd commands */
|
|
|
|
t_bool tq_suc (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint16 sts;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_suc\n");
|
|
|
|
if ((uptr = tq_getucb (lu))) { /* unit exist? */
|
|
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
|
sts = ST_OFL | SB_OFL_NV; /* offl no vol */
|
|
else {
|
|
sts = ST_SUC; /* avail or onl */
|
|
tq_setf_unit (pkt, uptr); /* hack flags */
|
|
}
|
|
tq_putr_unit (pkt, uptr, lu, TRUE); /* set fields */
|
|
}
|
|
else sts = ST_OFL; /* offline */
|
|
tq_putr (pkt, OP_SUC | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Flush - sequential nop - deferred if q'd cmds, bypassed if ser exc */
|
|
|
|
t_bool tq_flu (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint16 sts;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_flu\n");
|
|
|
|
if ((uptr = tq_getucb (lu))) /* unit exist? */
|
|
sts = tq_mot_valid (uptr, OP_FLU); /* validate req */
|
|
else sts = ST_OFL; /* offline */
|
|
tq_putr (pkt, OP_FLU | OP_END, tq_efl (uptr), sts, FLU_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Erase, erase gap - deferred if q'd cmds, bypassed if ser exc */
|
|
|
|
t_bool tq_erase (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */
|
|
uint16 sts;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_erase\n");
|
|
|
|
if ((uptr = tq_getucb (lu))) { /* unit exist? */
|
|
sts = tq_mot_valid (uptr, cmd); /* validity checks */
|
|
if (sts == ST_SUC) { /* ok? */
|
|
uptr->cpkt = pkt; /* op in progress */
|
|
uptr->iostarttime = sim_grtime();
|
|
sim_activate (uptr, 0); /* activate */
|
|
return OK; /* done */
|
|
}
|
|
}
|
|
else sts = ST_OFL; /* offline */
|
|
tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, ERS_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Write tape mark - deferred if q'd cmds, bypassed if ser exc */
|
|
|
|
t_bool tq_wtm (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint16 sts;
|
|
uint32 objp = 0;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_wtm\n");
|
|
|
|
if ((uptr = tq_getucb (lu))) { /* unit exist? */
|
|
objp = uptr->objp; /* position op */
|
|
sts = tq_mot_valid (uptr, OP_WTM); /* validity checks */
|
|
if (sts == ST_SUC) { /* ok? */
|
|
uptr->cpkt = pkt; /* op in progress */
|
|
uptr->iostarttime = sim_grtime();
|
|
sim_activate (uptr, 0); /* activate */
|
|
return OK; /* done */
|
|
}
|
|
}
|
|
else sts = ST_OFL; /* offline */
|
|
PUTP32 (pkt, WTM_POSL, objp); /* set obj pos */
|
|
tq_putr (pkt, OP_WTM | OP_END, tq_efl (uptr), sts, WTM_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Position - deferred if q'd cmds, bypassed if ser exc */
|
|
|
|
t_bool tq_pos (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint16 sts;
|
|
uint32 objp = 0;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_pos\n");
|
|
|
|
if ((uptr = tq_getucb (lu))) { /* unit exist? */
|
|
objp = uptr->objp; /* position op */
|
|
sts = tq_mot_valid (uptr, OP_POS); /* validity checks */
|
|
if (sts == ST_SUC) { /* ok? */
|
|
uptr->cpkt = pkt; /* op in progress */
|
|
if ((tq_pkt[pkt].d[CMD_MOD] & MD_RWD) && /* rewind? */
|
|
(!(tq_pkt[pkt].d[CMD_MOD] & MD_IMM))) { /* !immediate? */
|
|
sim_activate_after (uptr, tq_rwtime); /* use 2 sec rewind execute time */
|
|
}
|
|
else { /* otherwise */
|
|
uptr->iostarttime = sim_grtime();
|
|
sim_activate (uptr, 0); /* use normal execute time */
|
|
}
|
|
return OK; /* done */
|
|
}
|
|
}
|
|
else sts = ST_OFL; /* offline */
|
|
PUTP32 (pkt, POS_RCL, 0); /* clear #skipped */
|
|
PUTP32 (pkt, POS_TMCL, 0);
|
|
PUTP32 (pkt, POS_POSL, objp); /* set obj pos */
|
|
tq_putr (pkt, OP_POS | OP_END, tq_efl (uptr), sts, POS_LNT, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Data transfer commands - deferred if q'd commands, bypassed if ser exc */
|
|
|
|
t_bool tq_rw (uint16 pkt)
|
|
{
|
|
uint16 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
|
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */
|
|
uint32 bc = GETP32 (pkt, RW_BCL); /* byte count */
|
|
uint16 sts;
|
|
uint32 objp = 0;
|
|
UNIT *uptr;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_rw\n");
|
|
|
|
if ((uptr = tq_getucb (lu))) { /* unit exist? */
|
|
objp = uptr->objp; /* position op */
|
|
sts = tq_mot_valid (uptr, cmd); /* validity checks */
|
|
if (sts == ST_SUC) { /* ok? */
|
|
if ((bc == 0) || (bc > TQ_MAXFR)) { /* invalid? */
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
|
sts = ST_CMD | I_BCNT;
|
|
}
|
|
else {
|
|
uptr->cpkt = pkt; /* op in progress */
|
|
uptr->iostarttime = sim_grtime();
|
|
sim_activate (uptr, 0); /* activate */
|
|
return OK; /* done */
|
|
}
|
|
}
|
|
}
|
|
else sts = ST_OFL; /* offline */
|
|
PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */
|
|
PUTP32 (pkt, RW_POSL, objp); /* set obj pos */
|
|
PUTP32 (pkt, RW_RSZL, 0); /* clr rec size */
|
|
tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, RW_LNT_T, UQ_TYP_SEQ);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Validity checks */
|
|
|
|
uint16 tq_mot_valid (UNIT *uptr, uint32 cmd)
|
|
{
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_mot_valid\n");
|
|
|
|
if (uptr->flags & UNIT_SXC) /* ser exc pend? */
|
|
return ST_SXC;
|
|
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
|
return (ST_OFL | SB_OFL_NV); /* offl no vol */
|
|
if ((uptr->flags & UNIT_ONL) == 0) /* not online? */
|
|
return ST_AVL; /* only avail */
|
|
if (tq_cmf[cmd] & CMF_WR) { /* write op? */
|
|
if (uptr->uf & UF_WPS) { /* swre wlk? */
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
|
return (ST_WPR | SB_WPR_SW);
|
|
}
|
|
if (TQ_WPH (uptr)) { /* hwre wlk? */
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
|
return (ST_WPR | SB_WPR_HW);
|
|
}
|
|
}
|
|
return ST_SUC; /* success! */
|
|
}
|
|
|
|
/* Unit service for motion commands */
|
|
|
|
/* I/O completion callback */
|
|
|
|
void tq_io_complete (UNIT *uptr, t_stat status)
|
|
{
|
|
struct tq_req_results *res = (struct tq_req_results *)uptr->results;
|
|
|
|
sim_debug(DBG_TRC, &tq_dev, "tq_io_complete(status=%d)\n", status);
|
|
|
|
res->io_status = status;
|
|
res->io_complete = 1;
|
|
/* Reschedule for the appropriate delay */
|
|
sim_activate_notbefore (uptr, uptr->iostarttime+tq_xtime);
|
|
}
|
|
|
|
|
|
t_stat tq_svc (UNIT *uptr)
|
|
{
|
|
uint32 t;
|
|
t_mtrlnt wbc;
|
|
int32 pkt = uptr->cpkt; /* get packet */
|
|
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */
|
|
uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */
|
|
uint32 ba = GETP32 (pkt, RW_BAL); /* buf addr */
|
|
t_mtrlnt bc = GETP32 (pkt, RW_BCL); /* byte count */
|
|
uint32 nrec = GETP32 (pkt, POS_RCL); /* #rec to skip */
|
|
uint32 ntmk = GETP32 (pkt, POS_TMCL); /* #tmk to skp */
|
|
struct tq_req_results *res = (struct tq_req_results *)uptr->results;
|
|
int32 io_complete = res->io_complete;
|
|
|
|
sim_debug (DBG_TRC, &tq_dev, "tq_svc(unit=%d, pkt=%d, cmd=%s, mdf=0x%0X, bc=0x%0x, phase=%s)\n",
|
|
(int)(uptr-tq_dev.units), pkt, tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f], mdf, bc,
|
|
uptr->io_complete ? "bottom" : "top");
|
|
|
|
res->io_complete = 0;
|
|
if (pkt == 0) /* what??? */
|
|
return SCPE_IERR;
|
|
if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
|
|
tq_mot_end (uptr, 0, ST_OFL | SB_OFL_NV, 0); /* offl no vol */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
if (tq_cmf[cmd] & CMF_WR) { /* write op? */
|
|
if (TQ_WPH (uptr)) { /* hwre write prot? */
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
|
tq_mot_end (uptr, 0, ST_WPR | SB_WPR_HW, 0);
|
|
return SCPE_OK;
|
|
}
|
|
if (uptr->uf & UF_WPS) { /* swre write prot? */
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
|
tq_mot_end (uptr, 0, ST_WPR | SB_WPR_SW, 0);
|
|
return SCPE_OK;
|
|
}
|
|
}
|
|
if (!io_complete) {
|
|
res->sts = ST_SUC; /* assume success */
|
|
res->tbc = 0; /* assume zero rec */
|
|
}
|
|
switch (cmd) { /* case on command */
|
|
|
|
case OP_RD:case OP_ACC:case OP_CMP: /* read-like op */
|
|
if (!io_complete) {
|
|
if (mdf & MD_REV) /* read record */
|
|
tq_rdbufr_top (uptr, &res->tbc);
|
|
else
|
|
tq_rdbuff_top (uptr, &res->tbc);
|
|
return SCPE_OK;
|
|
}
|
|
if (mdf & MD_REV) /* read record */
|
|
res->sts = tq_rdbufr_bottom (uptr, &res->tbc);
|
|
else
|
|
res->sts = tq_rdbuff_bottom (uptr, &res->tbc);
|
|
if (res->sts == ST_DRV) { /* read error? */
|
|
PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */
|
|
return tq_mot_err (uptr, res->tbc); /* log, done */
|
|
}
|
|
if ((res->sts != ST_SUC) || (cmd == OP_ACC)) { /* error or access? */
|
|
if (res->sts == ST_TMK)
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
|
PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */
|
|
break;
|
|
}
|
|
if (res->tbc > bc) { /* tape rec > buf? */
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */
|
|
res->sts = ST_RDT; /* data truncated */
|
|
wbc = bc; /* set working bc */
|
|
}
|
|
else wbc = res->tbc;
|
|
if (cmd == OP_RD) { /* read? */
|
|
if ((t = Map_WriteB (ba, wbc, res->tqxb))) {/* store, nxm? */
|
|
PUTP32 (pkt, RW_BCL, wbc - t); /* adj bc */
|
|
if (tq_hbe (uptr, ba + wbc - t)) /* post err log */
|
|
tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, res->tbc);
|
|
return SCPE_OK; /* end if nxm */
|
|
}
|
|
} /* end if read */
|
|
else { /* compare */
|
|
uint8 mby, dby;
|
|
uint32 mba;
|
|
t_mtrlnt i;
|
|
for (i = 0; i < wbc; i++) { /* loop */
|
|
if (mdf & MD_REV) { /* reverse? */
|
|
mba = ba + bc - 1 - i; /* mem addr */
|
|
dby = ((uint8 *)res->tqxb)[res->tbc - 1 - i]; /* byte */
|
|
}
|
|
else {
|
|
mba = ba + i;
|
|
dby = ((uint8 *)res->tqxb)[i];
|
|
}
|
|
if (Map_ReadB (mba, 1, &mby)) { /* fetch, nxm? */
|
|
PUTP32 (pkt, RW_BCL, i); /* adj bc */
|
|
if (tq_hbe (uptr, mba)) /* post err log */
|
|
tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, res->tbc);
|
|
return SCPE_OK;
|
|
}
|
|
if (mby != dby) { /* cmp err? */
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* ser exc */
|
|
PUTP32 (pkt, RW_BCL, i); /* adj bc */
|
|
tq_mot_end (uptr, 0, ST_CMP, res->tbc);
|
|
return SCPE_OK; /* exit */
|
|
}
|
|
} /* end for */
|
|
} /* end if compare */
|
|
PUTP32 (pkt, RW_BCL, wbc); /* bytes read/cmp'd */
|
|
break;
|
|
|
|
case OP_WR: /* write */
|
|
if (!io_complete) { /* Top half processing */
|
|
if ((t = Map_ReadB (ba, bc, res->tqxb))) { /* fetch buf, nxm? */
|
|
PUTP32 (pkt, RW_BCL, 0); /* no bytes xfer'd */
|
|
if (tq_hbe (uptr, ba + bc - t)) /* post err log */
|
|
tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, bc);
|
|
return SCPE_OK; /* end else wr */
|
|
}
|
|
sim_tape_wrrecf_a (uptr, res->tqxb, bc, tq_io_complete); /* write rec fwd */
|
|
return SCPE_OK;
|
|
}
|
|
if (res->io_status)
|
|
return tq_mot_err (uptr, bc); /* log, end */
|
|
uptr->objp = uptr->objp + 1; /* upd obj pos */
|
|
if (TEST_EOT (uptr)) /* EOT on write? */
|
|
uptr->flags = uptr->flags | UNIT_SXC;
|
|
uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */
|
|
res->tbc = bc; /* RW_BC is ok */
|
|
break;
|
|
|
|
case OP_WTM: /* write tape mark */
|
|
if (!io_complete) { /* Top half processing */
|
|
sim_tape_wrtmk_a (uptr, tq_io_complete); /* write tmk, err? */
|
|
return SCPE_OK;
|
|
}
|
|
if (res->io_status)
|
|
return tq_mot_err (uptr, 0); /* log, end */
|
|
uptr->objp = uptr->objp + 1; /* incr obj cnt */
|
|
case OP_ERG: /* erase gap */
|
|
if (TEST_EOT (uptr)) /* EOT on write? */
|
|
uptr->flags = uptr->flags | UNIT_SXC;
|
|
uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */
|
|
break;
|
|
|
|
case OP_ERS: /* erase */
|
|
if (!io_complete) { /* Top half processing */
|
|
sim_tape_wreomrw_a (uptr, tq_io_complete); /* write eom, err? */
|
|
return SCPE_OK;
|
|
}
|
|
if (res->io_status)
|
|
return tq_mot_err (uptr, 0); /* log, end */
|
|
uptr->objp = 0;
|
|
uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL);
|
|
break;
|
|
|
|
case OP_POS: /* position */
|
|
if (!io_complete) { /* Top half processing */
|
|
res->sktmk = res->skrec = 0; /* clr skipped */
|
|
if (mdf & MD_RWD) { /* rewind? */
|
|
uptr->objp = 0; /* clr flags */
|
|
uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL);
|
|
}
|
|
sim_tape_position_a (uptr,
|
|
((mdf & MD_RWD) ? MTPOS_M_REW : 0) |
|
|
((mdf & MD_REV) ? MTPOS_M_REV : 0) |
|
|
((mdf & MD_OBC) ? MTPOS_M_OBJ : 0) |
|
|
(((mdf & MD_DLE) && !(mdf & MD_REV)) ? MTPOS_M_DLE : 0),
|
|
nrec, &res->skrec, ntmk, &res->sktmk, (uint32 *)&res->objupd, tq_io_complete);
|
|
return SCPE_OK;
|
|
}
|
|
res->sts = tq_map_status (uptr, res->io_status);
|
|
if ((res->io_status != MTSE_OK) && (res->io_status != MTSE_TMK) && (res->io_status != MTSE_BOT) && (res->io_status != MTSE_LEOT))
|
|
return tq_mot_err (uptr, 0); /* log, end */
|
|
sim_debug (DBG_REQ, &tq_dev, "Position Done: mdf=0x%04X, nrec=%d, ntmk=%d, skrec=%d, sktmk=%d, skobj=%d\n",
|
|
mdf, nrec, ntmk, res->skrec, res->sktmk, res->objupd);
|
|
if (mdf & MD_REV)
|
|
uptr->objp = uptr->objp - res->objupd;
|
|
else
|
|
uptr->objp = uptr->objp + res->objupd;
|
|
PUTP32 (pkt, POS_RCL, res->skrec); /* #rec skipped */
|
|
PUTP32 (pkt, POS_TMCL, res->sktmk); /* #tmk skipped */
|
|
break;
|
|
|
|
default:
|
|
return SCPE_IERR;
|
|
}
|
|
|
|
tq_mot_end (uptr, 0, (uint16)res->sts, res->tbc); /* done */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Motion command drive error */
|
|
|
|
t_stat tq_mot_err (UNIT *uptr, uint32 rsiz)
|
|
{
|
|
uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_TMK; /* serious exception */
|
|
if (tq_dte (uptr, ST_DRV)) /* post err log */
|
|
tq_mot_end (uptr, EF_LOG, ST_DRV, rsiz); /* if ok, report err */
|
|
return SCPE_IOERR;
|
|
}
|
|
|
|
/* Motion command complete */
|
|
|
|
t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint16 sts, uint32 rsiz)
|
|
{
|
|
uint16 pkt = uptr->cpkt; /* packet */
|
|
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */
|
|
uint16 lnt = RW_LNT_T; /* assume rw */
|
|
|
|
if (cmd == OP_ERG) /* set pkt lnt */
|
|
lnt = ERG_LNT;
|
|
else if (cmd == OP_ERS)
|
|
lnt = ERS_LNT;
|
|
else if (cmd == OP_WTM)
|
|
lnt = WTM_LNT;
|
|
else if (cmd == OP_POS)
|
|
lnt = POS_LNT;
|
|
|
|
uptr->cpkt = 0; /* done */
|
|
if (lnt > ERG_LNT) { /* xfer cmd? */
|
|
PUTP32 (pkt, RW_POSL, uptr->objp); /* position */
|
|
PUTP32 (pkt, RW_RSZL, rsiz); /* record size */
|
|
}
|
|
tq_putr (pkt, cmd | OP_END, flg | tq_efl (uptr), sts, lnt, UQ_TYP_SEQ);
|
|
if (!tq_putpkt (pkt, TRUE)) /* send pkt */
|
|
return ERR;
|
|
if (uptr->pktq) /* more to do? */
|
|
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* activate thread */
|
|
return OK;
|
|
}
|
|
|
|
/* Tape motion routines */
|
|
|
|
uint32 tq_map_status (UNIT *uptr, t_stat st)
|
|
{
|
|
switch (st) {
|
|
|
|
case MTSE_OK:
|
|
break;
|
|
|
|
case MTSE_UNATT:
|
|
uptr->flags = uptr->flags | UNIT_SXC;
|
|
return (ST_OFL | SB_OFL_NV);
|
|
|
|
case MTSE_FMT:
|
|
uptr->flags = uptr->flags | UNIT_SXC;
|
|
return ST_MFE;
|
|
|
|
case MTSE_TMK:
|
|
uptr->flags = uptr->flags | UNIT_SXC;
|
|
return ST_TMK;
|
|
|
|
case MTSE_INVRL:
|
|
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL;
|
|
return ST_FMT;
|
|
|
|
case MTSE_RECE:
|
|
case MTSE_IOERR:
|
|
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL;
|
|
return ST_DRV;
|
|
|
|
case MTSE_EOM:
|
|
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL;
|
|
return ST_DAT;
|
|
|
|
case MTSE_BOT:
|
|
uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_POL;
|
|
return ST_BOT;
|
|
|
|
case MTSE_WRP:
|
|
uptr->flags = uptr->flags | UNIT_SXC;
|
|
return ST_WPR;
|
|
|
|
case MTSE_LEOT:
|
|
return ST_LED;
|
|
}
|
|
|
|
return ST_SUC;
|
|
}
|
|
|
|
/* Read buffer - can return ST_TMK, ST_FMT, or ST_DRV */
|
|
|
|
void tq_rdbuff_top (UNIT *uptr, t_mtrlnt *tbc)
|
|
{
|
|
struct tq_req_results *res = (struct tq_req_results *)uptr->results;
|
|
|
|
sim_tape_rdrecf_a (uptr, res->tqxb, tbc, MT_MAXFR, tq_io_complete);/* read rec fwd */
|
|
}
|
|
|
|
uint32 tq_rdbuff_bottom (UNIT *uptr, t_mtrlnt *tbc)
|
|
{
|
|
t_stat st;
|
|
struct tq_req_results *res = (struct tq_req_results *)uptr->results;
|
|
|
|
st = res->io_status; /* read rec fwd io status */
|
|
if (st == MTSE_TMK) { /* tape mark? */
|
|
uptr->flags = uptr->flags | UNIT_SXC | UNIT_TMK; /* serious exc */
|
|
uptr->objp = uptr->objp + 1; /* update obj cnt */
|
|
return ST_TMK;
|
|
}
|
|
if (st != MTSE_OK) /* other error? */
|
|
return tq_map_status (uptr, st);
|
|
uptr->flags = uptr->flags & ~UNIT_TMK; /* clr tape mark */
|
|
uptr->objp = uptr->objp + 1; /* upd obj cnt */
|
|
return ST_SUC;
|
|
}
|
|
|
|
void tq_rdbufr_top (UNIT *uptr, t_mtrlnt *tbc)
|
|
{
|
|
struct tq_req_results *res = (struct tq_req_results *)uptr->results;
|
|
|
|
sim_tape_rdrecr_a (uptr, res->tqxb, tbc, MT_MAXFR, tq_io_complete); /* read rec rev */
|
|
}
|
|
|
|
uint32 tq_rdbufr_bottom (UNIT *uptr, t_mtrlnt *tbc)
|
|
{
|
|
t_stat st;
|
|
struct tq_req_results *res = (struct tq_req_results *)uptr->results;
|
|
|
|
st = res->io_status; /* read rec rev io status */
|
|
if (st == MTSE_TMK) { /* tape mark? */
|
|
uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */
|
|
uptr->objp = uptr->objp - 1; /* update obj cnt */
|
|
return ST_TMK;
|
|
}
|
|
if (st != MTSE_OK) /* other error? */
|
|
return tq_map_status (uptr, st);
|
|
uptr->objp = uptr->objp - 1; /* upd obj cnt */
|
|
return ST_SUC;
|
|
}
|
|
|
|
/* Data transfer error log packet */
|
|
|
|
t_bool tq_dte (UNIT *uptr, uint16 err)
|
|
{
|
|
uint16 pkt, tpkt;
|
|
uint16 lu;
|
|
|
|
if ((tq_cflgs & CF_THS) == 0) /* logging? */
|
|
return OK;
|
|
if (!tq_deqf (&pkt)) /* get log pkt */
|
|
return ERR;
|
|
tpkt = uptr->cpkt; /* rw pkt */
|
|
lu = tq_pkt[tpkt].d[CMD_UN]; /* unit # */
|
|
|
|
tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */
|
|
tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */
|
|
tq_pkt[pkt].d[ELP_UN] = lu; /* copy unit */
|
|
tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */
|
|
tq_pkt[pkt].d[DTE_CIDA] = 0; /* ctrl ID */
|
|
tq_pkt[pkt].d[DTE_CIDB] = 0;
|
|
tq_pkt[pkt].d[DTE_CIDC] = 0;
|
|
tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) |
|
|
(drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD);
|
|
tq_pkt[pkt].d[DTE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */
|
|
tq_pkt[pkt].d[DTE_MLUN] = lu; /* MLUN */
|
|
tq_pkt[pkt].d[DTE_UIDA] = lu; /* unit ID */
|
|
tq_pkt[pkt].d[DTE_UIDB] = 0;
|
|
tq_pkt[pkt].d[DTE_UIDC] = 0;
|
|
tq_pkt[pkt].d[DTE_UIDD] = (UID_TAPE << DTE_UIDD_V_CLS) |
|
|
(drv_tab[tq_typ].umod << DTE_UIDD_V_MOD);
|
|
tq_pkt[pkt].d[DTE_UVER] = drv_tab[tq_typ].uver; /* unit ver */
|
|
PUTP32 (pkt, DTE_POSL, uptr->objp); /* position */
|
|
tq_pkt[pkt].d[DTE_FVER] = drv_tab[tq_typ].fver; /* fmtr ver */
|
|
tq_putr (pkt, FM_TAP, LF_SNR, err, DTE_LNT, UQ_TYP_DAT);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Host bus error log packet */
|
|
|
|
t_bool tq_hbe (UNIT *uptr, uint32 ba)
|
|
{
|
|
uint16 pkt, tpkt;
|
|
|
|
if ((tq_cflgs & CF_THS) == 0) /* logging? */
|
|
return OK;
|
|
if (!tq_deqf (&pkt)) /* get log pkt */
|
|
return ERR;
|
|
tpkt = uptr->cpkt; /* rw pkt */
|
|
tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */
|
|
tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */
|
|
tq_pkt[pkt].d[ELP_UN] = tq_pkt[tpkt].d[CMD_UN]; /* copy unit */
|
|
tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */
|
|
tq_pkt[pkt].d[HBE_CIDA] = 0; /* ctrl ID */
|
|
tq_pkt[pkt].d[HBE_CIDB] = 0;
|
|
tq_pkt[pkt].d[HBE_CIDC] = 0;
|
|
tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) |
|
|
(drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD);
|
|
tq_pkt[pkt].d[HBE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */
|
|
tq_pkt[pkt].d[HBE_RSV] = 0;
|
|
PUTP32 (pkt, HBE_BADL, ba); /* bad addr */
|
|
tq_putr (pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Port last failure error log packet */
|
|
|
|
t_bool tq_plf (uint32 err)
|
|
{
|
|
uint16 pkt = 0;
|
|
|
|
if (!tq_deqf (&pkt)) /* get log pkt */
|
|
return ERR;
|
|
tq_pkt[pkt].d[ELP_REFL] = tq_pkt[pkt].d[ELP_REFH] = 0; /* ref = 0 */
|
|
tq_pkt[pkt].d[ELP_UN] = tq_pkt[pkt].d[ELP_SEQ] = 0; /* no unit, seq */
|
|
tq_pkt[pkt].d[PLF_CIDA] = 0; /* cntl ID */
|
|
tq_pkt[pkt].d[PLF_CIDB] = 0;
|
|
tq_pkt[pkt].d[PLF_CIDC] = 0;
|
|
tq_pkt[pkt].d[PLF_CIDD] = (TQ_CLASS << PLF_CIDD_V_CLS) |
|
|
(drv_tab[tq_typ].cmod << PLF_CIDD_V_MOD);
|
|
tq_pkt[pkt].d[PLF_VER] = drv_tab[tq_typ].cver;
|
|
tq_pkt[pkt].d[PLF_ERR] = (uint16)err;
|
|
tq_putr (pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT);
|
|
tq_pkt[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID);
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* Unit now available attention packet */
|
|
|
|
t_bool tq_una (UNIT *uptr)
|
|
{
|
|
uint16 pkt;
|
|
uint16 lu;
|
|
|
|
if (!tq_deqf (&pkt)) /* get log pkt */
|
|
return ERR;
|
|
lu = (uint16) (uptr->unit_plug); /* get unit */
|
|
tq_pkt[pkt].d[RSP_REFL] = tq_pkt[pkt].d[RSP_REFH] = 0; /* ref = 0 */
|
|
tq_pkt[pkt].d[RSP_UN] = lu;
|
|
tq_pkt[pkt].d[RSP_RSV] = 0;
|
|
tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */
|
|
tq_putr (pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ); /* fill std fields */
|
|
return tq_putpkt (pkt, TRUE);
|
|
}
|
|
|
|
/* List handling
|
|
|
|
tq_deqf - dequeue head of free list (fatal err if none)
|
|
tq_deqh - dequeue head of list
|
|
tq_enqh - enqueue at head of list
|
|
tq_enqt - enqueue at tail of list
|
|
*/
|
|
|
|
t_bool tq_deqf (uint16 *pkt)
|
|
{
|
|
if (tq_freq == 0) /* no free pkts?? */
|
|
return tq_fatal (PE_NSR);
|
|
tq_pbsy = tq_pbsy + 1; /* cnt busy pkts */
|
|
*pkt = tq_freq; /* head of list */
|
|
tq_freq = tq_pkt[tq_freq].link; /* next */
|
|
return OK;
|
|
}
|
|
|
|
uint16 tq_deqh (uint16 *lh)
|
|
{
|
|
int16 ptr = *lh; /* head of list */
|
|
|
|
if (ptr) /* next */
|
|
*lh = tq_pkt[ptr].link;
|
|
return ptr;
|
|
}
|
|
|
|
void tq_enqh (uint16 *lh, int16 pkt)
|
|
{
|
|
if (pkt == 0) /* any pkt? */
|
|
return;
|
|
tq_pkt[pkt].link = *lh; /* link is old lh */
|
|
*lh = pkt; /* pkt is new lh */
|
|
return;
|
|
}
|
|
|
|
void tq_enqt (uint16 *lh, int16 pkt)
|
|
{
|
|
if (pkt == 0) /* any pkt? */
|
|
return;
|
|
tq_pkt[pkt].link = 0; /* it will be tail */
|
|
if (*lh == 0) /* if empty, enqh */
|
|
*lh = pkt;
|
|
else {
|
|
uint16 ptr = *lh; /* chase to end */
|
|
while (tq_pkt[ptr].link)
|
|
ptr = tq_pkt[ptr].link;
|
|
tq_pkt[ptr].link = pkt; /* enq at tail */
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Packet and descriptor handling */
|
|
|
|
/* Get packet from command ring */
|
|
|
|
t_bool tq_getpkt (uint16 *pkt)
|
|
{
|
|
uint32 addr, desc;
|
|
|
|
if (!tq_getdesc (&tq_cq, &desc)) /* get cmd desc */
|
|
return ERR;
|
|
if ((desc & UQ_DESC_OWN) == 0) { /* none */
|
|
*pkt = 0; /* pkt = 0 */
|
|
return OK; /* no error */
|
|
}
|
|
if (!tq_deqf (pkt)) /* get cmd pkt */
|
|
return ERR;
|
|
tq_hat = 0; /* dsbl hst timer */
|
|
addr = desc & UQ_ADDR; /* get Q22 addr */
|
|
if (Map_ReadW (addr + UQ_HDR_OFF, TQ_PKT_SIZE, tq_pkt[*pkt].d))
|
|
return tq_fatal (PE_PRE); /* read pkt */
|
|
return tq_putdesc (&tq_cq, desc); /* release desc */
|
|
}
|
|
|
|
/* Put packet to response ring - note the clever hack about credits.
|
|
The controller sends all its credits to the host. Thereafter, it
|
|
supplies one credit for every response packet sent over. Simple!
|
|
*/
|
|
|
|
t_bool tq_putpkt (uint16 pkt, t_bool qt)
|
|
{
|
|
uint32 addr, desc, lnt, cr;
|
|
UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]);
|
|
|
|
if (pkt == 0) /* any packet? */
|
|
return OK;
|
|
if (up)
|
|
sim_debug (DBG_REQ, &tq_dev, "rsp=%04X, sts=%04X, rszl=%04X, obj=%d, pos=%" T_ADDR_FMT "d\n",
|
|
tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS], tq_pkt[pkt].d[RW_RSZL],
|
|
up->objp, up->pos);
|
|
else
|
|
sim_debug (DBG_REQ, &tq_dev, "rsp=%04X, sts=%04X\n",
|
|
tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS]);
|
|
if (!tq_getdesc (&tq_rq, &desc)) /* get rsp desc */
|
|
return ERR;
|
|
if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */
|
|
if (qt) /* normal? q tail */
|
|
tq_enqt (&tq_rspq, pkt);
|
|
else tq_enqh (&tq_rspq, pkt); /* resp q call */
|
|
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* activate q thrd */
|
|
return OK;
|
|
}
|
|
addr = desc & UQ_ADDR; /* get Q22 addr */
|
|
lnt = tq_pkt[pkt].d[UQ_HLNT] - UQ_HDR_OFF; /* size, with hdr */
|
|
if ((GETP (pkt, UQ_HCTC, TYP) == UQ_TYP_SEQ) && /* seq packet? */
|
|
(GETP (pkt, CMD_OPC, OPC) & OP_END)) { /* end packet? */
|
|
cr = (tq_credits >= 14)? 14: tq_credits; /* max 14 credits */
|
|
tq_credits = tq_credits - cr; /* decr credits */
|
|
tq_pkt[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR);
|
|
}
|
|
if (Map_WriteW (addr + UQ_HDR_OFF, lnt, tq_pkt[pkt].d))
|
|
return tq_fatal (PE_PWE); /* write pkt */
|
|
tq_enqh (&tq_freq, pkt); /* pkt is free */
|
|
tq_pbsy = tq_pbsy - 1; /* decr busy cnt */
|
|
if (tq_pbsy == 0) /* idle? strt hst tmr */
|
|
tq_hat = tq_htmo;
|
|
return tq_putdesc (&tq_rq, desc); /* release desc */
|
|
}
|
|
|
|
/* Get a descriptor from the host */
|
|
|
|
t_bool tq_getdesc (struct uq_ring *ring, uint32 *desc)
|
|
{
|
|
uint32 addr = ring->ba + ring->idx;
|
|
uint16 d[2];
|
|
|
|
if (Map_ReadW (addr, 4, d)) /* fetch desc */
|
|
return tq_fatal (PE_QRE); /* err? dead */
|
|
*desc = ((uint32) d[0]) | (((uint32) d[1]) << 16);
|
|
return OK;
|
|
}
|
|
|
|
/* Return a descriptor to the host, clearing owner bit
|
|
If rings transitions from "empty" to "not empty" or "full" to
|
|
"not full", and interrupt bit was set, interrupt the host.
|
|
Actually, test whether previous ring entry was owned by host.
|
|
*/
|
|
|
|
t_bool tq_putdesc (struct uq_ring *ring, uint32 desc)
|
|
{
|
|
uint32 prvd, newd = (desc & ~UQ_DESC_OWN) | UQ_DESC_F;
|
|
uint32 prva, addr = ring->ba + ring->idx;
|
|
uint16 d[2];
|
|
|
|
d[0] = newd & 0xFFFF; /* 32b to 16b */
|
|
d[1] = (newd >> 16) & 0xFFFF;
|
|
if (Map_WriteW (addr, 4, d)) /* store desc */
|
|
return tq_fatal (PE_QWE); /* err? dead */
|
|
if (desc & UQ_DESC_F) { /* was F set? */
|
|
if (ring->lnt <= 4) /* lnt = 1? intr */
|
|
tq_ring_int (ring);
|
|
else {
|
|
prva = ring->ba + /* prv desc */
|
|
((ring->idx - 4) & (ring->lnt - 1));
|
|
if (Map_ReadW (prva, 4, d)) /* read prv */
|
|
return tq_fatal (PE_QRE);
|
|
prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16);
|
|
if (prvd & UQ_DESC_OWN)
|
|
tq_ring_int (ring);
|
|
}
|
|
}
|
|
ring->idx = (ring->idx + 4) & (ring->lnt - 1);
|
|
return OK;
|
|
}
|
|
|
|
/* Get unit descriptor for logical unit - trivial now,
|
|
but eventually, hide multiboard complexities here */
|
|
|
|
UNIT *tq_getucb (uint16 lu)
|
|
{
|
|
uint32 i;
|
|
UNIT *uptr;
|
|
|
|
for (i = 0; i < tq_dev.numunits - 2; i++) {
|
|
uptr = &tq_dev.units[i];
|
|
if ((lu == uptr->unit_plug) &&
|
|
!(uptr->flags & UNIT_DIS))
|
|
return uptr;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Hack unit flags */
|
|
|
|
void tq_setf_unit (int16 pkt, UNIT *uptr)
|
|
{
|
|
uptr->uf = tq_pkt[pkt].d[ONL_UFL] & UF_MSK; /* settable flags */
|
|
if ((tq_pkt[pkt].d[CMD_MOD] & MD_SWP) && /* swre wrp enb? */
|
|
(tq_pkt[pkt].d[ONL_UFL] & UF_WPS)) /* swre wrp on? */
|
|
uptr->uf = uptr->uf | UF_WPS; /* simon says... */
|
|
return;
|
|
}
|
|
|
|
/* Hack end flags */
|
|
|
|
uint32 tq_efl (UNIT *uptr)
|
|
{
|
|
uint32 t = 0;
|
|
|
|
if (uptr) { /* any unit? */
|
|
if (uptr->flags & UNIT_POL) /* note pos lost */
|
|
t = t | EF_PLS;
|
|
if (uptr->flags & UNIT_SXC) /* note ser exc */
|
|
t = t | EF_SXC;
|
|
if (TEST_EOT (uptr)) /* note EOT */
|
|
t = t | EF_EOT;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/* Unit response fields */
|
|
|
|
void tq_putr_unit (int16 pkt, UNIT *uptr, uint16 lu, t_bool all)
|
|
{
|
|
tq_pkt[pkt].d[ONL_MLUN] = (uint16)lu; /* multi-unit */
|
|
tq_pkt[pkt].d[ONL_UFL] = (uint16)(uptr->uf | TQ_WPH (uptr));/* unit flags */
|
|
tq_pkt[pkt].d[ONL_UFL] |= tq_efl (uptr); /* end flags accordingly */
|
|
tq_pkt[pkt].d[ONL_RSVL] = tq_pkt[pkt].d[ONL_RSVH] = 0; /* reserved */
|
|
tq_pkt[pkt].d[ONL_UIDA] = (uint16)lu; /* UID low */
|
|
tq_pkt[pkt].d[ONL_UIDB] = 0;
|
|
tq_pkt[pkt].d[ONL_UIDC] = 0;
|
|
tq_pkt[pkt].d[ONL_UIDD] = (UID_TAPE << ONL_UIDD_V_CLS) |
|
|
(drv_tab[tq_typ].umod << ONL_UIDD_V_MOD); /* UID hi */
|
|
PUTP32 (pkt, ONL_MEDL, drv_tab[tq_typ].med); /* media type */
|
|
if (all) { /* if long form */
|
|
tq_pkt[pkt].d[ONL_FMT] = drv_tab[tq_typ].fmt; /* format */
|
|
tq_pkt[pkt].d[ONL_SPD] = 0; /* speed */
|
|
PUTP32 (pkt, ONL_MAXL, TQ_MAXFR); /* max xfr */
|
|
tq_pkt[pkt].d[ONL_NREC] = 0; /* noise rec */
|
|
tq_pkt[pkt].d[ONL_RSVE] = 0; /* reserved */
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* UQ_HDR and RSP_OP fields */
|
|
|
|
void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint16 sts, uint16 lnt, uint16 typ)
|
|
{
|
|
tq_pkt[pkt].d[RSP_OPF] = (uint16)((cmd << RSP_OPF_V_OPC) |/* set cmd, flg */
|
|
(flg << RSP_OPF_V_FLG));
|
|
tq_pkt[pkt].d[RSP_STS] = sts;
|
|
tq_pkt[pkt].d[UQ_HLNT] = lnt; /* length */
|
|
tq_pkt[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) | /* type, cid */
|
|
(UQ_CID_TMSCP << UQ_HCTC_V_CID); /* clr credits */
|
|
return;
|
|
}
|
|
|
|
/* Post interrupt during init */
|
|
|
|
void tq_init_int (void)
|
|
{
|
|
if ((tq_s1dat & SA_S1H_IE) && tq_dib.vec)
|
|
SET_INT (TQ);
|
|
return;
|
|
}
|
|
|
|
/* Post interrupt during putpkt - note that NXMs are ignored! */
|
|
|
|
void tq_ring_int (struct uq_ring *ring)
|
|
{
|
|
uint32 iadr = tq_comm + ring->ioff; /* addr intr wd */
|
|
uint16 flag = 1;
|
|
|
|
(void)Map_WriteW (iadr, 2, &flag); /* write flag */
|
|
if (tq_dib.vec) /* if enb, intr */
|
|
SET_INT (TQ);
|
|
return;
|
|
}
|
|
|
|
/* Return interrupt vector */
|
|
|
|
int32 tq_inta (void)
|
|
{
|
|
return tq_dib.vec; /* prog vector */
|
|
}
|
|
|
|
/* Fatal error */
|
|
|
|
t_bool tq_fatal (uint32 err)
|
|
{
|
|
sim_debug (DBG_TRC, &tq_dev, "tq_fatal\n");
|
|
|
|
sim_debug (DBG_REQ, &tq_dev, "fatal err=%X\n", err);
|
|
tq_reset (&tq_dev); /* reset device */
|
|
tq_sa = SA_ER | err; /* SA = dead code */
|
|
tq_csta = CST_DEAD; /* state = dead */
|
|
tq_perr = err; /* save error */
|
|
return ERR;
|
|
}
|
|
|
|
/* Device attach */
|
|
|
|
t_stat tq_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_stat r;
|
|
|
|
r = sim_tape_attach_ex (uptr, cptr, DBG_TAP, 0);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
if (tq_csta == CST_UP)
|
|
uptr->flags = (uptr->flags | UNIT_ATP) & ~(UNIT_SXC | UNIT_POL | UNIT_TMK);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Device detach */
|
|
|
|
t_stat tq_detach (UNIT *uptr)
|
|
{
|
|
t_stat r;
|
|
|
|
r = sim_tape_detach (uptr); /* detach unit */
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP | UNIT_SXC | UNIT_POL | UNIT_TMK);
|
|
uptr->uf = 0; /* clr unit flgs */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Device reset */
|
|
|
|
t_stat tq_reset (DEVICE *dptr)
|
|
{
|
|
int32 i, j;
|
|
UNIT *uptr;
|
|
static t_bool plugs_inited = FALSE;
|
|
|
|
for (i=tq_max_plug=0; i<TQ_NUMDR; i++)
|
|
if (dptr->units[i].unit_plug > tq_max_plug)
|
|
tq_max_plug = (uint16)dptr->units[i].unit_plug;
|
|
if (!plugs_inited ) {
|
|
uint32 d;
|
|
char uname[16];
|
|
|
|
sprintf (uname, "%s-TIMER", dptr->name);
|
|
sim_set_uname (&dptr->units[4], uname);
|
|
sprintf (uname, "%s-QUESVC", dptr->name);
|
|
sim_set_uname (&dptr->units[5], uname);
|
|
plugs_inited = TRUE;
|
|
for (d = 0; d < tq_dev.numunits - 2; d++)
|
|
tq_unit[d].unit_plug = d;
|
|
}
|
|
|
|
tq_csta = CST_S1; /* init stage 1 */
|
|
tq_s1dat = 0; /* no S1 data */
|
|
tq_dib.vec = 0; /* no vector */
|
|
if (UNIBUS) /* Unibus? */
|
|
tq_sa = SA_S1 | SA_S1C_DI | SA_S1C_MP;
|
|
else tq_sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP; /* init SA val */
|
|
tq_cflgs = CF_RPL; /* ctrl flgs off */
|
|
tq_htmo = TQ_DHTMO; /* default timeout */
|
|
tq_hat = tq_htmo; /* default timer */
|
|
tq_cq.ba = tq_cq.lnt = tq_cq.idx = 0; /* clr cmd ring */
|
|
tq_rq.ba = tq_rq.lnt = tq_rq.idx = 0; /* clr rsp ring */
|
|
tq_credits = (TQ_NPKTS / 2) - 1; /* init credits */
|
|
tq_freq = 1; /* init free list */
|
|
for (i = 0; i < TQ_NPKTS; i++) { /* all pkts free */
|
|
if (i)
|
|
tq_pkt[i].link = (i + 1) & TQ_M_NPKTS;
|
|
else tq_pkt[i].link = 0;
|
|
for (j = 0; j < TQ_PKT_SIZE_W; j++)
|
|
tq_pkt[i].d[j] = 0;
|
|
}
|
|
tq_rspq = 0; /* no q'd rsp pkts */
|
|
tq_pbsy = 0; /* all pkts free */
|
|
tq_pip = 0; /* not polling */
|
|
CLR_INT (TQ); /* clr intr req */
|
|
for (i = 0; i < TQ_NUMDR + 2; i++) { /* init units */
|
|
uptr = tq_dev.units + i;
|
|
sim_cancel (uptr); /* clr activity */
|
|
sim_tape_reset (uptr);
|
|
uptr->flags = uptr->flags & /* not online */
|
|
~(UNIT_ONL|UNIT_ATP|UNIT_SXC|UNIT_POL|UNIT_TMK);
|
|
uptr->uf = 0; /* clr unit flags */
|
|
uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */
|
|
if (uptr->results == NULL)
|
|
uptr->results = calloc (1, sizeof (struct tq_req_results));
|
|
if (uptr->results == NULL)
|
|
return SCPE_MEM;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Device bootstrap */
|
|
|
|
#if defined (VM_PDP11)
|
|
|
|
#define BOOT_START 016000 /* start */
|
|
#define BOOT_ENTRY (BOOT_START + 002) /* entry */
|
|
#define BOOT_UNIT (BOOT_START + 010) /* unit number */
|
|
#define BOOT_CSR (BOOT_START + 014) /* CSR */
|
|
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
|
|
|
|
/* Data structure definitions */
|
|
|
|
#define B_CMDINT (BOOT_START - 01000) /* cmd int */
|
|
#define B_RSPINT (B_CMDINT + 002) /* rsp int */
|
|
#define B_RING (B_RSPINT + 002) /* ring base */
|
|
#define B_RSPH (B_RING + 010) /* resp pkt hdr */
|
|
#define B_TKRSP (B_RSPH + 004) /* resp pkt */
|
|
#define B_CMDH (B_TKRSP + 060) /* cmd pkt hdr */
|
|
#define B_TKCMD (B_CMDH + 004) /* cmd pkt */
|
|
#define B_UNIT (B_TKCMD + 004) /* unit # */
|
|
|
|
static const uint16 boot_rom[] = {
|
|
|
|
0046525, /* ST: "UM" */
|
|
|
|
0012706, 0016000, /* mov #st,sp */
|
|
0012700, 0000000, /* mov #unitno,r0 */
|
|
0012701, 0174500, /* mov #174500,r1 ; ip addr */
|
|
0005021, /* clr (r1)+ ; init */
|
|
0012704, 0004000, /* mov #4000,r4 ; s1 mask */
|
|
0005002, /* clr r2 */
|
|
0005022, /* 10$: clr (r2)+ ; clr up to boot */
|
|
0020237, BOOT_START - 2, /* cmp r2,#st-2 */
|
|
0103774, /* blo 10$ */
|
|
0012705, BOOT_START+0312, /* mov #cmdtbl,r5 ; addr of tbl */
|
|
|
|
/* Four step init process */
|
|
|
|
0005711, /* 20$: tst (r1) ; err? */
|
|
0100001, /* bpl 30$ */
|
|
0000000, /* halt */
|
|
0030411, /* 30$: bit r4,(r1) ; step set? */
|
|
0001773, /* beq 20$ ; wait */
|
|
0012511, /* mov (r5)+,(r1) ; send next */
|
|
0006304, /* asl r4 ; next mask */
|
|
0100370, /* bpl 20$ ; s4 done? */
|
|
|
|
/* Set up rings, issue ONLINE, REWIND, READ */
|
|
|
|
0012737, 0000400, B_CMDH + 2, /* mov #400,cmdh+2 ; VCID = 1 */
|
|
0012737, 0000044, B_CMDH, /* mov #36.,cmdh ; cmd pkt lnt */
|
|
0010037, B_UNIT, /* mov r0,unit ; unit # */
|
|
0012737, 0000011, B_TKCMD + 8, /* mov #11,tkcmd+8. ; online op */
|
|
0012737, 0020000, B_TKCMD + 10, /* mov #20000,tkcmd+10. ; clr ser ex */
|
|
0012702, B_RING, /* mov #ring,r2 ; init rings */
|
|
0012722, B_TKRSP, /* mov #tkrsp,(r2)+ ; rsp pkt addr */
|
|
0010203, /* mov r2,r3 ; save ring+2 */
|
|
0010423, /* mov r4,(r3)+ ; set TK own */
|
|
0012723, B_TKCMD, /* mov #tkcmd,(r3)+ ; cmd pkt addr */
|
|
0010423, /* mov r4,(r3)+ ; set TK own */
|
|
0005741, /* tst -(r1) ; start poll */
|
|
0005712, /* 40$: tst (r2) ; wait for resp */
|
|
0100776, /* bmi 40$ */
|
|
0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */
|
|
0001401, /* beq 50$ */
|
|
0000000, /* halt */
|
|
0012703, B_TKCMD + 8, /* 50$: mov #tkcmd+8.,r3 */
|
|
0012723, 0000045, /* mov #45,(r3)+ ; reposition */
|
|
0012723, 0020002, /* mov #20002,(r3)+ ; rew, clr exc */
|
|
0012723, 0000001, /* mov #1,(r3)+ ; lo rec skp */
|
|
0005023, /* clr (r3)+ ; hi rec skp */
|
|
0005023, /* clr (r3)+ ; lo tmk skp */
|
|
0005023, /* clr (r3)+ ; hi tmk skp */
|
|
0010412, /* mov r4,(r2) ; TK own rsp */
|
|
0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */
|
|
0005711, /* tst (r1) ; start poll */
|
|
0005712, /* 60$: tst (r2) ; wait for resp */
|
|
0100776, /* bmi 60$ */
|
|
0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */
|
|
0001401, /* beq 70$ */
|
|
0000000, /* halt */
|
|
0012703, B_TKCMD + 8, /* 70$: mov #tkcmd+8.,r3 */
|
|
0012723, 0000041, /* mov #41,(r3)+ ; read */
|
|
0012723, 0020000, /* mov #20000,(r3)+ ; clr exc */
|
|
0012723, 0001000, /* mov #512.,(r3)+ ; bc = 512 */
|
|
0005023, /* clr (r3)+ ; clr args */
|
|
0005023, /* clr (r3)+ ; ba = 0 */
|
|
0010412, /* mov r4,(r2) ; TK own rsp */
|
|
0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */
|
|
0005711, /* tst (r1) ; start poll */
|
|
0005712, /* 80$: tst (r2) ; wait for resp */
|
|
0100776, /* bmi 80$ */
|
|
0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */
|
|
0001401, /* beq 90$ */
|
|
0000000, /* halt */
|
|
|
|
/* Boot block read in, jump to 0 - leave controller init'd */
|
|
|
|
0005003, /* clr r3 */
|
|
0012704, BOOT_START+020, /* mov #st+020,r4 */
|
|
0005005, /* clr r5 */
|
|
0005007, /* clr pc */
|
|
|
|
0100000, /* cmdtbl: init step 1 */
|
|
B_RING, /* ring base */
|
|
0000000, /* high ring base */
|
|
0000001 /* go */
|
|
};
|
|
|
|
t_stat tq_boot (int32 unitno, DEVICE *dptr)
|
|
{
|
|
size_t i;
|
|
UNIT *uptr = &dptr->units[unitno];
|
|
|
|
for (i = 0; i < BOOT_LEN; i++)
|
|
WrMemW (BOOT_START + (2 * i), boot_rom[i]);
|
|
WrMemW (BOOT_UNIT, (uint16)uptr->unit_plug);
|
|
WrMemW (BOOT_CSR, tq_dib.ba & DMASK);
|
|
cpu_set_boot (BOOT_ENTRY);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
#else
|
|
|
|
t_stat tq_boot (int32 unitno, DEVICE *dptr)
|
|
{
|
|
return SCPE_NOFNC;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Special show commands */
|
|
|
|
void tq_show_ring (FILE *st, struct uq_ring *rp)
|
|
{
|
|
uint32 i, desc;
|
|
uint16 d[2];
|
|
|
|
#if defined (VM_PDP11)
|
|
fprintf (st, "ring, base = %o, index = %d, length = %d\n",
|
|
rp->ba, rp->idx >> 2, rp->lnt >> 2);
|
|
#else
|
|
fprintf (st, "ring, base = %x, index = %d, length = %d\n",
|
|
rp->ba, rp->idx >> 2, rp->lnt >> 2);
|
|
#endif
|
|
for (i = 0; i < (rp->lnt >> 2); i++) {
|
|
if (Map_ReadW (rp->ba + (i << 2), 4, d)) {
|
|
fprintf (st, " %3d: non-existent memory\n", i);
|
|
break;
|
|
}
|
|
desc = ((uint32) d[0]) | (((uint32) d[1]) << 16);
|
|
#if defined (VM_PDP11)
|
|
fprintf (st, " %3d: %011o\n", i, desc);
|
|
#else
|
|
fprintf (st, " %3d: %08x\n", i, desc);
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
|
|
void tq_show_pkt (FILE *st, int32 pkt)
|
|
{
|
|
int32 i, j;
|
|
uint32 cr = GETP (pkt, UQ_HCTC, CR);
|
|
uint32 typ = GETP (pkt, UQ_HCTC, TYP);
|
|
uint32 cid = GETP (pkt, UQ_HCTC, CID);
|
|
|
|
fprintf (st, "packet %d, credits = %d, type = %d, cid = %d\n",
|
|
pkt, cr, typ, cid);
|
|
for (i = 0; i < TQ_SH_MAX; i = i + TQ_SH_PPL) {
|
|
fprintf (st, " %2d:", i);
|
|
for (j = i; j < (i + TQ_SH_PPL); j++)
|
|
#if defined (VM_PDP11)
|
|
fprintf (st, " %06o", tq_pkt[pkt].d[j]);
|
|
#else
|
|
fprintf (st, " %04x", tq_pkt[pkt].d[j]);
|
|
#endif
|
|
fprintf (st, "\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
t_stat tq_show_unitq (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
int32 pkt, u = uptr - tq_dev.units;
|
|
|
|
if (tq_csta != CST_UP) {
|
|
fprintf (st, "Controller is not initialized\n");
|
|
return SCPE_OK;
|
|
}
|
|
if ((uptr->flags & UNIT_ONL) == 0) {
|
|
if (uptr->flags & UNIT_ATT)
|
|
fprintf (st, "Unit %d is available\n", u);
|
|
else fprintf (st, "Unit %d is offline\n", u);
|
|
return SCPE_OK;
|
|
}
|
|
if (uptr->cpkt) {
|
|
fprintf (st, "Unit %d current ", u);
|
|
tq_show_pkt (st, uptr->cpkt);
|
|
if ((pkt = uptr->pktq)) {
|
|
do {
|
|
fprintf (st, "Unit %d queued ", u);
|
|
tq_show_pkt (st, pkt);
|
|
} while ((pkt = tq_pkt[pkt].link));
|
|
}
|
|
}
|
|
else fprintf (st, "Unit %d queues are empty\n", u);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat tq_show_ctrl (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
int32 i, pkt;
|
|
|
|
if (tq_csta != CST_UP) {
|
|
fprintf (st, "Controller is not initialized\n");
|
|
return SCPE_OK;
|
|
}
|
|
if (val & TQ_SH_RI) {
|
|
if (tq_pip)
|
|
fprintf (st, "Polling in progress, host timer = %d\n", tq_hat);
|
|
else fprintf (st, "Host timer = %d\n", tq_hat);
|
|
fprintf (st, "Command ");
|
|
tq_show_ring (st, &tq_cq);
|
|
fprintf (st, "Response ");
|
|
tq_show_ring (st, &tq_rq);
|
|
}
|
|
if (val & TQ_SH_FR) {
|
|
if ((pkt = tq_freq)) {
|
|
for (i = 0; pkt != 0; i++, pkt = tq_pkt[pkt].link) {
|
|
if (i == 0)
|
|
fprintf (st, "Free queue = %d", pkt);
|
|
else if ((i % 16) == 0)
|
|
fprintf (st, ",\n %d", pkt);
|
|
else fprintf (st, ", %d", pkt);
|
|
}
|
|
fprintf (st, "\n");
|
|
}
|
|
else fprintf (st, "Free queue is empty\n");
|
|
}
|
|
if (val & TQ_SH_RS) {
|
|
if ((pkt = tq_rspq)) {
|
|
do {
|
|
fprintf (st, "Response ");
|
|
tq_show_pkt (st, pkt);
|
|
} while ((pkt = tq_pkt[pkt].link));
|
|
}
|
|
else fprintf (st, "Response queue is empty\n");
|
|
}
|
|
if (val & TQ_SH_UN) {
|
|
for (i = 0; i < TQ_NUMDR; i++)
|
|
tq_show_unitq (st, &tq_unit[i], 0, NULL);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set controller type (and capacity for user-defined type) */
|
|
|
|
t_stat tq_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
uint32 i, cap;
|
|
uint32 max = sim_taddr_64? TQU_EMAXC: TQU_MAXC;
|
|
t_stat r;
|
|
|
|
if ((val < 0) || (val > TQU_TYPE) || ((val != TQU_TYPE) && cptr))
|
|
return SCPE_ARG;
|
|
for (i = 0; i < TQ_NUMDR; i++) {
|
|
if (tq_unit[i].flags & UNIT_ATT)
|
|
return SCPE_ALATT;
|
|
}
|
|
if (cptr) {
|
|
cap = (uint32) get_uint (cptr, 10, max, &r);
|
|
if ((r != SCPE_OK) || (cap < TQU_MINC))
|
|
return SCPE_ARG;
|
|
drv_tab[TQU_TYPE].cap = ((t_addr) cap) << 20;
|
|
}
|
|
tq_typ = val;
|
|
for (i = 0; i < TQ_NUMDR; i++)
|
|
tq_unit[i].capac = drv_tab[tq_typ].cap;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Show controller type and capacity */
|
|
|
|
t_stat tq_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
fprintf (st, "%s (%dMB)", drv_tab[tq_typ].name, (uint32) (drv_tab[tq_typ].cap >> 20));
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Show unit plug */
|
|
|
|
t_stat tq_show_plug (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
fprintf (st, "UNIT=%d", uptr->unit_plug);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set unit plug */
|
|
|
|
t_stat tq_set_plug (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
int32 plug;
|
|
uint32 i;
|
|
t_stat r;
|
|
DEVICE *dptr = find_dev_from_unit (uptr);
|
|
|
|
if (cptr == NULL)
|
|
return sim_messagef (SCPE_ARG, "Must specify UNIT=value\n");
|
|
plug = (int32) get_uint (cptr, 10, 0xFFFFFFFF, &r);
|
|
if ((r != SCPE_OK) || (plug > 65534))
|
|
return sim_messagef (SCPE_ARG, "Invalid Unit Plug Number: %s\n", cptr);
|
|
if (uptr->unit_plug == plug)
|
|
return SCPE_OK;
|
|
for (i=0; i < dptr->numunits - 2; i++)
|
|
if (dptr->units[i].unit_plug == plug)
|
|
return sim_messagef (SCPE_ARG, "Unit Plug %d Already In Use on %s\n", plug, sim_uname (&dptr->units[i]));
|
|
uptr->unit_plug = plug;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat tq_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
const char *devtype = UNIBUS ? "TUK50" : "TQK50";
|
|
|
|
fprintf (st, "%s (TQ)\n\n", tq_description (dptr));
|
|
fprintf (st, "The TQ controller simulates the %s TMSCP disk controller. TQ options\n", devtype);
|
|
fprintf (st, "include the ability to set units write enabled or write locked, and to\n");
|
|
fprintf (st, "specify the controller type and tape length:\n");
|
|
fprint_set_help (st, dptr);
|
|
fprintf (st, "\nThe %s device supports the BOOT command.\n", devtype);
|
|
fprint_show_help (st, dptr);
|
|
fprint_reg_help (st, dptr);
|
|
fprintf (st, "\nError handling is as follows:\n\n");
|
|
fprintf (st, " error processed as\n");
|
|
fprintf (st, " not attached tape not ready\n\n");
|
|
fprintf (st, " end of file end of medium\n");
|
|
fprintf (st, " OS I/O error fatal tape error\n\n");
|
|
sim_tape_attach_help (st, dptr, uptr, flag, cptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
const char *tq_description (DEVICE *dptr)
|
|
{
|
|
return (UNIBUS) ? "TUK50 TMSCP magnetic tape controller" :
|
|
"TQK50 TMSCP magnetic tape controller";
|
|
}
|