/* pdp11_rq.c: MSCP disk controller simulator

   Copyright (c) 2002-2004, Robert M Supnik
   Derived from work by Stephen F. Shirron

   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.

   rq		RQDX3 disk controller

   05-Feb-04	RMS	Revised for file I/O library
   25-Jan-04	RMS	Revised for device debug support
   12-Jan-04	RMS	Fixed bug in interrupt control (found by Tom Evans)
   07-Oct-03	RMS	Fixed problem with multiple RAUSER drives
   17-Sep-03	RMS	Fixed MB to LBN conversion to be more accurate
   11-Jul-03	RMS	Fixed bug in user disk size (found by Chaskiel M Grundman)
   19-May-03	RMS	Revised for new conditional compilation scheme
   25-Apr-03	RMS	Revised for extended file support
   14-Mar-03	RMS	Fixed variable size interaction with save/restore
   27-Feb-03	RMS	Added 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
   12-Oct-02	RMS	Added multicontroller support
   29-Sep-02	RMS	Changed addressing to 18b in Unibus mode
			Added variable address support to bootstrap
			Added vector display support
			Fixed status code in HBE error log
			Consolidated MSCP/TMSCP header file
			New data structures
   16-Aug-02	RMS	Removed unused variables (found by David Hittner)
   04-May-02	RMS	Fixed bug in polling loop for queued operations
   26-Mar-02	RMS	Fixed bug, reset routine cleared UF_WPH
   09-Mar-02	RMS	Adjusted delays for M+ timing bugs
   04-Mar-02	RMS	Added delays to initialization for M+, RSTS/E
   16-Feb-02	RMS	Fixed bugs in host timeout logic, boot
   26-Jan-02	RMS	Revised bootstrap to conform to M9312
   06-Jan-02	RMS	Revised enable/disable support
   30-Dec-01	RMS	Revised show routines
   19-Dec-01	RMS	Added bigger drives
   17-Dec-01	RMS	Added queue process
*/

#if defined (VM_PDP10)					/* PDP10 version */
#error "RQDX3 not supported on PDP-10!"

#elif defined (VM_VAX)					/* VAX version */
#include "vax_defs.h"
#define RQ_AINC		4
#define RQ_WID		32
extern int32 int_req[IPL_HLVL];
extern int32 int_vec[IPL_HLVL][32];

#else							/* PDP-11 version */
#include "pdp11_defs.h"
#define RQ_AINC		2
#define RQ_WID		16
extern int32 cpu_18b, cpu_ubm;
extern int32 int_req[IPL_HLVL];
extern int32 int_vec[IPL_HLVL][32];
#endif

#if !defined (RQ_NUMCT)
#define RQ_NUMCT	4
#elif (RQ_NUMCT > 4)
#error "Assertion failure: RQ_NUMCT exceeds 4"
#endif

#include "pdp11_uqssp.h"
#include "pdp11_mscp.h"

#define	UF_MSK		(UF_CMR|UF_CMW)			/* settable flags */

#define RQ_SH_MAX	24				/* max display wds */
#define RQ_SH_PPL	8				/* wds per line */
#define RQ_SH_DPL	4				/* desc per line */
#define RQ_SH_RI	001				/* show rings */
#define RQ_SH_FR	002				/* show free q */
#define RQ_SH_RS	004				/* show resp q */
#define RQ_SH_UN	010				/* show unit q's */

#define RQ_CLASS	1				/* RQ class */
#define RQ_UQPM		19				/* UQ port model */
#define RQ_MODEL	19				/* MSCP ctrl model */
#define RQ_HVER		1				/* hardware version */
#define RQ_SVER		3				/* software version */
#define RQ_DHTMO	60				/* def host timeout */
#define RQ_DCTMO	120				/* def ctrl timeout */
#define RQ_NUMDR	4				/* # drives */
#define RQ_NUMBY	512				/* bytes per block */
#define RQ_MAXFR	(1 << 16)			/* max xfer */

#define UNIT_V_ONL	(UNIT_V_UF + 0)			/* online */
#define UNIT_V_WLK	(UNIT_V_UF + 1)			/* hwre write lock */
#define UNIT_V_ATP	(UNIT_V_UF + 2)			/* attn pending */
#define UNIT_V_DTYPE	(UNIT_V_UF + 3)			/* drive type */
#define UNIT_M_DTYPE	0xF
#define UNIT_ONL	(1 << UNIT_V_ONL)
#define UNIT_WLK	(1 << UNIT_V_WLK)
#define UNIT_ATP	(1 << UNIT_V_ATP)
#define UNIT_DTYPE	(UNIT_M_DTYPE << UNIT_V_DTYPE)
#define GET_DTYPE(x)	(((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
#define cpkt		u3				/* current packet */
#define pktq		u4				/* packet queue */
#define uf		buf				/* settable unit flags */
#define cnum		wait				/* controller index */
#define UNIT_WPRT	(UNIT_WLK | UNIT_RO)		/* write prot */
#define RQ_RMV(u)	((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RMV)? \
			 UF_RMV: 0)
#define RQ_WPH(u)	(((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RO) || \
			  (u->flags & UNIT_WPRT))? UF_WPH: 0)

#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 ERR		0				/* must be SCPE_OK! */
#define OK		1

#define RQ_TIMER	(RQ_NUMDR)
#define RQ_QUEUE	(RQ_TIMER + 1)

/* Internal packet management.  The real RQDX3 manages its packets as true
   linked lists.  However, use of actual addresses in structures won't work
   with save/restore.  Accordingly, the packets are an arrayed structure,
   and links are actually subscripts.  To minimize complexity, packet[0]
   is not used (0 = end of list), and the number of packets must be a power
   of two.
*/

#define RQ_NPKTS	32				/* # packets (pwr of 2) */
#define RQ_M_NPKTS	(RQ_NPKTS - 1)			/* mask */
#define RQ_PKT_SIZE_W	32				/* payload size (wds) */
#define RQ_PKT_SIZE	(RQ_PKT_SIZE_W * sizeof (int16))

struct rqpkt {
	int16	link;					/* link to next */
	uint16	d[RQ_PKT_SIZE_W];  };			/* data */

/* Packet payload extraction and insertion; cp defines controller */

#define GETP(p,w,f)	((cp->pak[p].d[w] >> w##_V_##f) & w##_M_##f)
#define GETP32(p,w)	(((uint32) cp->pak[p].d[w]) | \
			(((uint32) cp->pak[p].d[(w)+1]) << 16))
#define PUTP32(p,w,x)	cp->pak[p].d[w] = (x) & 0xFFFF; \
			cp->pak[p].d[(w)+1] = ((x) >> 16) & 0xFFFF

/* Disk formats.  An RQDX3 consists of the following regions:

   XBNs		Extended blocks - contain information about disk format,
		also holds track being reformatted during bad block repl.
		Size = sectors/track + 1, replicated 3 times.
   DBNs		Diagnostic blocks - used by diagnostics.  Sized to pad
		out the XBNs to a cylinder boundary.
   LBNs		Logical blocks - contain user information.
   RCT		Replacement control table - first block contains status,
		second contains data from block being replaced, remaining
		contain information about replaced bad blocks.
		Size = RBNs/128 + 3, replicated 4-8 times.
   RBNs		Replacement blocks - used to replace bad blocks.

   The simulator does not need to perform bad block replacement; the
   information below is for simulating RCT reads, if required.

   Note that an RA drive has a different order: LBNs, RCT, XBN, DBN;
   the RBNs are spare blocks at the end of every track.
*/

#define RCT_OVHD	2				/* #ovhd blks */
#define RCT_ENTB	128				/* entries/blk */
#define RCT_END		0x80000000			/* marks RCT end */

/* The RQDX3 supports multiple disk drive types:

   type	sec	surf	cyl	tpg	gpc	RCT	LBNs
	
   RX50	10	1	80	5	16	-	800
   RX33	15	2	80	2	1	-	2400
   RD51	18	4	306	4	1	36*4	21600
   RD31	17	4	615	4	1	3*8	41560
   RD52	17	8	512	8	1	4*8	60480
   RD53	17	7	1024	7	1	5*8	138672
   RD54	17	15	1225	15	1	7*8	311200

   The simulator also supports larger drives that only existed
   on SDI controllers.  XBN, DBN, RCTS and RCTC are not known
   for the SDI drives and are not used by the simulator:

   RA82	57	15	1435	15	1	?*8	1216665
   RA72	51	20	1921?	20	1	?*8	1953300
   RA90	69	13	2656	13	1	?*8	2376153
   RA92	73	13	3101	13	1	?*8	2940951

   Each drive can be a different type.  The drive field in the
   unit flags specified the drive type and thus, indirectly,
   the drive size.
*/

#define RQDF_RMV	01				/* removable */
#define RQDF_RO		02				/* read only */
#define RQDF_SDI	04				/* SDI drive */

#define RX50_DTYPE	0
#define RX50_SECT	10
#define RX50_SURF	1
#define RX50_CYL	80
#define RX50_TPG	5
#define RX50_GPC	16
#define RX50_XBN	0
#define RX50_DBN	0
#define RX50_LBN	800
#define RX50_RCTS	0
#define RX50_RCTC	0
#define RX50_RBN	0
#define RX50_MOD	7
#define RX50_MED	0x25658032
#define RX50_FLGS	RQDF_RMV

#define RX33_DTYPE	1
#define RX33_SECT	15
#define RX33_SURF	2
#define RX33_CYL	80
#define RX33_TPG	2
#define RX33_GPC	1
#define RX33_XBN	0
#define RX33_DBN	0
#define RX33_LBN	2400
#define RX33_RCTS	0
#define RX33_RCTC	0
#define RX33_RBN	0
#define RX33_MOD	10
#define RX33_MED	0x25658021
#define RX33_FLGS	RQDF_RMV

#define RD51_DTYPE	2
#define RD51_SECT	18
#define RD51_SURF	4
#define RD51_CYL	306
#define RD51_TPG	4
#define RD51_GPC	1
#define RD51_XBN	57
#define RD51_DBN	87
#define RD51_LBN	21600
#define RD51_RCTS	36
#define RD51_RCTC	4
#define RD51_RBN	144
#define RD51_MOD	6
#define RD51_MED	0x25644033
#define RD51_FLGS	0

#define RD31_DTYPE	3
#define RD31_SECT	17
#define RD31_SURF	4
#define RD31_CYL	615				/* last unused */
#define RD31_TPG	RD31_SURF
#define RD31_GPC	1
#define RD31_XBN	54
#define RD31_DBN	14
#define RD31_LBN	41560
#define RD31_RCTS	3
#define RD31_RCTC	8
#define RD31_RBN	100
#define RD31_MOD	12
#define RD31_MED	0x2564401F
#define RD31_FLGS	0

#define RD52_DTYPE	4				/* Quantum params */
#define RD52_SECT	17
#define RD52_SURF	8
#define RD52_CYL	512
#define RD52_TPG	RD52_SURF
#define RD52_GPC	1
#define RD52_XBN	54
#define RD52_DBN	82
#define RD52_LBN	60480
#define RD52_RCTS	4
#define RD52_RCTC	8
#define RD52_RBN	168
#define RD52_MOD	8
#define RD52_MED	0x25644034
#define RD52_FLGS	0

#define RD53_DTYPE	5
#define RD53_SECT	17
#define RD53_SURF	8
#define RD53_CYL	1024				/* last unused */
#define RD53_TPG	RD53_SURF
#define RD53_GPC	1
#define RD53_XBN	54
#define RD53_DBN	82
#define RD53_LBN	138672
#define RD53_RCTS	5
#define RD53_RCTC	8
#define RD53_RBN	280
#define RD53_MOD	9
#define RD53_MED	0x25644035
#define RD53_FLGS	0

#define RD54_DTYPE	6
#define RD54_SECT	17
#define RD54_SURF	15
#define RD54_CYL	1225				/* last unused */
#define RD54_TPG	RD54_SURF
#define RD54_GPC	1
#define RD54_XBN	54
#define RD54_DBN	201
#define RD54_LBN	311200
#define RD54_RCTS	7
#define RD54_RCTC	8
#define RD54_RBN	609
#define RD54_MOD	13
#define RD54_MED	0x25644036
#define RD54_FLGS	0

#define RA82_DTYPE	7				/* SDI drive */
#define RA82_SECT	57				/* +1 spare/track */
#define RA82_SURF	15
#define RA82_CYL	1435				/* 0-1422 user */
#define RA82_TPG	RA82_SURF
#define RA82_GPC	1
#define RA82_XBN	3420				/* cyl 1427-1430 */
#define RA82_DBN	3420				/* cyl 1431-1434 */
#define RA82_LBN	1216665				/* 57*15*1423 */
#define RA82_RCTS	400				/* cyl 1423-1426 */
#define RA82_RCTC	8
#define RA82_RBN	21345				/* 1 *15*1423 */
#define RA82_MOD	11
#define RA82_MED	0x25641052
#define RA82_FLGS	RQDF_SDI

#define RRD40_DTYPE	8
#define RRD40_SECT	128
#define RRD40_SURF	1
#define RRD40_CYL	10400
#define RRD40_TPG	RRD40_SURF
#define RRD40_GPC	1
#define RRD40_XBN	0
#define RRD40_DBN	0
#define RRD40_LBN	1331200
#define RRD40_RCTS	0
#define RRD40_RCTC	0
#define RRD40_RBN	0
#define RRD40_MOD	26
#define RRD40_MED	0x25652228
#define RRD40_FLGS	(RQDF_RMV | RQDF_RO)

#define RA72_DTYPE	9				/* SDI drive */
#define RA72_SECT	51				/* +1 spare/trk */
#define RA72_SURF	20
#define RA72_CYL	1921				/* 0-1914 user */
#define RA72_TPG	RA72_SURF
#define RA72_GPC	1
#define RA72_XBN	2040				/* cyl 1917-1918? */
#define RA72_DBN	2040				/* cyl 1920-1921? */
#define RA72_LBN	1953300				/* 51*20*1915 */
#define RA72_RCTS	400				/* cyl 1915-1916? */
#define RA72_RCTC	5				/* ? */
#define RA72_RBN	38300				/* 1 *20*1915 */
#define RA72_MOD	37
#define RA72_MED	0x25641048
#define RA72_FLGS	RQDF_SDI

#define RA90_DTYPE	10				/* SDI drive */
#define RA90_SECT	69				/* +1 spare/trk */
#define RA90_SURF	13
#define RA90_CYL	2656				/* 0-2648 user */
#define RA90_TPG	RA90_SURF
#define RA90_GPC	1
#define RA90_XBN	1794				/* cyl 2651-2652? */
#define RA90_DBN	1794				/* cyl 2653-2654? */
#define RA90_LBN	2376153				/* 69*13*2649 */
#define RA90_RCTS	400				/* cyl 2649-2650? */
#define RA90_RCTC	6				/* ? */
#define RA90_RBN	34437				/* 1 *13*2649 */
#define RA90_MOD	19
#define RA90_MED	0x2564105A
#define RA90_FLGS	RQDF_SDI

#define RA92_DTYPE	11				/* SDI drive */
#define RA92_SECT	73				/* +1 spare/trk */
#define RA92_SURF	13
#define RA92_CYL	3101				/* 0-3098 user */
#define RA92_TPG	RA92_SURF
#define RA92_GPC	1
#define RA92_XBN	174				/* cyl 3100? */
#define RA92_DBN	775
#define RA92_LBN	2940951				/* 73*13*3099 */
#define RA92_RCTS	316				/* cyl 3099? */
#define RA92_RCTC	3				/* ? */
#define RA92_RBN	40287				/* 1 *13*3099 */
#define RA92_MOD	29
#define RA92_MED	0x2564105C
#define RA92_FLGS	RQDF_SDI

#define RA8U_DTYPE	12				/* user defined */
#define RA8U_SECT	57				/* +1 spare/track */
#define RA8U_SURF	15
#define RA8U_CYL	1435				/* 0-1422 user */
#define RA8U_TPG	RA8U_SURF
#define RA8U_GPC	1
#define RA8U_XBN	3420				/* cyl 1427-1430 */
#define RA8U_DBN	3420				/* cyl 1431-1434 */
#define RA8U_LBN	1216665				/* 57*15*1423 */
#define RA8U_RCTS	400				/* cyl 1423-1426 */
#define RA8U_RCTC	8
#define RA8U_RBN	21345				/* 1 *15*1423 */
#define RA8U_MOD	11				/* RA82 */
#define RA8U_MED	0x25641052			/* RA82 */
#define RA8U_FLGS	RQDF_SDI
#define RA8U_MINC	5				/* min cap MB */
#define RA8U_MAXC	2000				/* max cap MB */
#define RA8U_EMAXC	1000000				/* ext max cap */

struct drvtyp {
	int32	sect;					/* sectors */
	int32	surf;					/* surfaces */
	int32	cyl;					/* cylinders */
	int32	tpg;					/* trk/grp */
	int32	gpc;					/* grp/cyl */
	int32	xbn;					/* XBN size */
	int32	dbn;					/* DBN size */
	uint32	lbn;					/* LBN size */
	int32	rcts;					/* RCT size */
	int32	rctc;					/* RCT copies */
	int32	rbn;					/* RBNs */
	int32	mod;					/* MSCP model */
	int32	med;					/* MSCP media */
	int32	flgs;					/* flags */
	char	*name;					/* name */
};

#define RQ_DRV(d) \
	d##_SECT, d##_SURF, d##_CYL,  d##_TPG, \
	d##_GPC,  d##_XBN,  d##_DBN,  d##_LBN, \
	d##_RCTS, d##_RCTC, d##_RBN,  d##_MOD, \
	d##_MED, d##_FLGS
#define RQ_SIZE(d)	(d##_LBN * RQ_NUMBY)

static struct drvtyp drv_tab[] = {
	{ RQ_DRV (RX50), "RX50" }, { RQ_DRV (RX33), "RX33" },
	{ RQ_DRV (RD51), "RD51" }, { RQ_DRV (RD31), "RD31" },
	{ RQ_DRV (RD52), "RD52" }, { RQ_DRV (RD53), "RD53" },
	{ RQ_DRV (RD54), "RD54" }, { RQ_DRV (RA82), "RA82" },
	{ RQ_DRV (RRD40), "RRD40" }, { RQ_DRV (RA72), "RA72" },
	{ RQ_DRV (RA90), "RA90" }, { RQ_DRV (RA92), "RA92" },
	{ RQ_DRV (RA8U), "RAUSER" }, { 0 }  };

extern int32 int_req[IPL_HLVL];
extern int32 tmr_poll, clk_tps;
extern UNIT cpu_unit;
extern FILE *sim_deb;
extern uint32 sim_taddr_64;

uint16 *rqxb = NULL;					/* xfer buffer */
int32 rq_itime = 200;					/* init time, except */
int32 rq_itime4 = 10;					/* stage 4 */
int32 rq_qtime = 200;					/* queue time */
int32 rq_xtime = 500;					/* transfer time */

struct mscp_con {
	uint32		cnum;				/* ctrl number */
	uint32		ubase;				/* unit base */
	uint32		sa;				/* status, addr */
	uint32		saw;				/* written data */
	uint32		s1dat;				/* S1 data */
	uint32		comm;				/* comm region */
	uint32		csta;				/* ctrl state */
	uint32		perr;				/* last error */
	uint32		cflgs;				/* ctrl flags */
	uint32		irq;				/* intr request */
	uint32		prgi;				/* purge int */
	uint32		pip;				/* poll in progress */
	int32		freq;				/* free list */
	int32		rspq;				/* resp list */
	uint32		pbsy;				/* #busy pkts */
	uint32		credits;			/* credits */
	uint32		hat;				/* host timer */
	uint32		htmo;				/* host timeout */
	struct uq_ring	cq;				/* cmd ring */
	struct uq_ring	rq;				/* rsp ring */
	struct rqpkt	pak[RQ_NPKTS];			/* packet queue */
};

#define MSC	struct mscp_con				/* MSCP ctrl context */

DEVICE rq_dev, rqb_dev, rqc_dev,rqd_dev;

t_stat rq_rd (int32 *data, int32 PA, int32 access);
t_stat rq_wr (int32 data, int32 PA, int32 access);
t_stat rq_svc (UNIT *uptr);
t_stat rq_tmrsvc (UNIT *uptr);
t_stat rq_quesvc (UNIT *uptr);
t_stat rq_reset (DEVICE *dptr);
t_stat rq_attach (UNIT *uptr, char *cptr);
t_stat rq_detach (UNIT *uptr);
t_stat rq_boot (int32 unitno, DEVICE *dptr);
t_stat rq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat rq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat rq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat rq_show_wlk (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat rq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat rq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc);

t_bool rq_step4 (MSC *cp);
t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q);
t_bool rq_abo (MSC *cp, int32 pkt, t_bool q);
t_bool rq_avl (MSC *cp, int32 pkt, t_bool q);
t_bool rq_fmt (MSC *cp, int32 pkt, t_bool q);
t_bool rq_gcs (MSC *cp, int32 pkt, t_bool q);
t_bool rq_gus (MSC *cp, int32 pkt, t_bool q);
t_bool rq_onl (MSC *cp, int32 pkt, t_bool q);
t_bool rq_rw (MSC *cp, int32 pkt, t_bool q);
t_bool rq_scc (MSC *cp, int32 pkt, t_bool q);
t_bool rq_suc (MSC *cp, int32 pkt, t_bool q);
t_bool rq_plf (MSC *cp, uint32 err);
t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err);
t_bool rq_hbe (MSC *cp, UNIT *uptr);
t_bool rq_una (MSC *cp, int32 un);
t_bool rq_deqf (MSC *cp, int32 *pkt);
int32 rq_deqh (MSC *cp, int32 *lh);
void rq_enqh (MSC *cp, int32 *lh, int32 pkt);
void rq_enqt (MSC *cp, int32 *lh, int32 pkt);
t_bool rq_getpkt (MSC *cp, int32 *pkt);
t_bool rq_putpkt (MSC *cp, int32 pkt, t_bool qt);
t_bool rq_getdesc (MSC *cp, struct uq_ring *ring, uint32 *desc);
t_bool rq_putdesc (MSC *cp, struct uq_ring *ring, uint32 desc);
int32 rq_rw_valid (MSC *cp, int32 pkt, UNIT *uptr, uint32 cmd);
t_bool rq_rw_end (MSC *cp, UNIT *uptr, uint32 flg, uint32 sts);
void rq_putr (MSC *cp, int32 pkt, uint32 cmd, uint32 flg,
	uint32 sts, uint32 lnt, uint32 typ);
void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all);
void rq_setf_unit (MSC *cp, int32 pkt, UNIT *uptr);
void rq_init_int (MSC *cp);
void rq_ring_int (MSC *cp, struct uq_ring *ring);
t_bool rq_fatal (MSC *cp, uint32 err);
UNIT *rq_getucb (MSC *cp, uint32 lu);
int32 rq_map_pa (uint32 pa);
void rq_setint (MSC *cp);
void rq_clrint (MSC *cp);
int32 rq_inta (void);

/* RQ data structures

   rq_dev	RQ device descriptor
   rq_unit	RQ unit list
   rq_reg	RQ register list
   rq_mod	RQ modifier list
*/

MSC rq_ctx = { 0 };

DIB rq_dib = { IOBA_RQ, IOLN_RQ, &rq_rd, &rq_wr,
		1, IVCL (RQ), 0, { &rq_inta } };

UNIT rq_unit[] = {
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RX50_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RX50)) },
	{ UDATA (&rq_tmrsvc, UNIT_DIS, 0) },
	{ UDATA (&rq_quesvc, UNIT_DIS, 0) }  };

REG rq_reg[] = {
	{ GRDATA (SA, rq_ctx.sa, DEV_RDX, 16, 0) },
	{ GRDATA (SAW, rq_ctx.saw, DEV_RDX, 16, 0) },
	{ GRDATA (S1DAT, rq_ctx.s1dat, DEV_RDX, 16, 0) },
	{ GRDATA (COMM, rq_ctx.comm, DEV_RDX, 22, 0) },
	{ GRDATA (CQBA, rq_ctx.cq.ba, DEV_RDX, 22, 0) },
	{ GRDATA (CQLNT, rq_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ },
	{ GRDATA (CQIDX, rq_ctx.cq.idx, DEV_RDX, 8, 2) },
	{ GRDATA (RQBA, rq_ctx.rq.ba, DEV_RDX, 22, 0) },
	{ GRDATA (RQLNT, rq_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ },
	{ GRDATA (RQIDX, rq_ctx.rq.idx, DEV_RDX, 8, 2) },
	{ DRDATA (FREE, rq_ctx.freq, 5) },
	{ DRDATA (RESP, rq_ctx.rspq, 5) },
	{ DRDATA (PBSY, rq_ctx.pbsy, 5) },
	{ GRDATA (CFLGS, rq_ctx.cflgs, DEV_RDX, 16, 0) },
	{ GRDATA (CSTA, rq_ctx.csta, DEV_RDX, 4, 0) },
	{ GRDATA (PERR, rq_ctx.perr, DEV_RDX, 9, 0) },
	{ DRDATA (CRED, rq_ctx.credits, 5) },
	{ DRDATA (HAT, rq_ctx.hat, 17) },
	{ DRDATA (HTMO, rq_ctx.htmo, 17) },
	{ FLDATA (PRGI, rq_ctx.prgi, 0), REG_HIDDEN },
	{ FLDATA (PIP, rq_ctx.pip, 0), REG_HIDDEN },
	{ FLDATA (INT, rq_ctx.irq, 0) },
	{ DRDATA (ITIME, rq_itime, 24), PV_LEFT + REG_NZ },
	{ DRDATA (I4TIME, rq_itime4, 24), PV_LEFT + REG_NZ },
	{ DRDATA (QTIME, rq_qtime, 24), PV_LEFT + REG_NZ },
	{ DRDATA (XTIME, rq_xtime, 24), PV_LEFT + REG_NZ },
	{ BRDATA (PKTS, rq_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) },
	{ URDATA (CPKT, rq_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) },
	{ URDATA (PKTQ, rq_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) },
	{ URDATA (UFLG, rq_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) },
	{ URDATA (CAPAC, rq_unit[0].capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) },
	{ GRDATA (DEVADDR, rq_dib.ba, DEV_RDX, 32, 0), REG_HRO },
	{ GRDATA (DEVVEC, rq_dib.vec, DEV_RDX, 16, 0), REG_HRO },
	{ DRDATA (DEVLBN, drv_tab[RA8U_DTYPE].lbn, 22), REG_HRO },
	{ NULL }  };

MTAB rq_mod[] = {
	{ UNIT_WLK, 0, NULL, "WRITEENABLED", &rq_set_wlk },
	{ UNIT_WLK, UNIT_WLK, NULL, "LOCKED", &rq_set_wlk },
	{ MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_RI, "RINGS", NULL,
		NULL, &rq_show_ctrl, 0 },
	{ MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_FR, "FREEQ", NULL,
		NULL, &rq_show_ctrl, 0 },
	{ MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_RS, "RESPQ", NULL,
		NULL, &rq_show_ctrl, 0 },
	{ MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_UN, "UNITQ", NULL,
		NULL, &rq_show_ctrl, 0 },
	{ MTAB_XTD | MTAB_VDV | MTAB_NMO, -1, "ALL", NULL,
		NULL, &rq_show_ctrl, 0 },
	{ MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "UNITQ", NULL,
		NULL, &rq_show_unitq, 0 },
	{ MTAB_XTD | MTAB_VUN, 0, "WRITE", NULL,
		NULL, &rq_show_wlk, NULL },
	{ MTAB_XTD | MTAB_VUN, RX50_DTYPE, NULL, "RX50",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RX33_DTYPE, NULL, "RX33",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RD31_DTYPE, NULL, "RD31",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RD51_DTYPE, NULL, "RD51",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RD52_DTYPE, NULL, "RD52",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RD53_DTYPE, NULL, "RD53",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RD54_DTYPE, NULL, "RD54",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RA82_DTYPE, NULL, "RA82",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RRD40_DTYPE, NULL, "RRD40",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RRD40_DTYPE, NULL, "CDROM",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RA72_DTYPE, NULL, "RA72",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RA90_DTYPE, NULL, "RA90",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RA92_DTYPE, NULL, "RA92",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, RA8U_DTYPE, NULL, "RAUSER",
		&rq_set_type, NULL, NULL },
	{ MTAB_XTD | MTAB_VUN, 0, "TYPE", NULL,
		NULL, &rq_show_type, NULL },
#if defined (VM_PDP11)
	{ MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",
		&set_addr, &show_addr, NULL },
#else
	{ MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL,
		NULL, &show_addr, NULL },
#endif
	{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL,
		NULL, &show_vec, NULL },
	{ MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE",
		&set_addr_flt, NULL, NULL },
	{ 0 }  };

DEVICE rq_dev = {
	"RQ", rq_unit, rq_reg, rq_mod,
	RQ_NUMDR + 2, DEV_RDX, 31, RQ_AINC, DEV_RDX, RQ_WID,
	NULL, NULL, &rq_reset,
	&rq_boot, &rq_attach, &rq_detach,
	&rq_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG };

/* RQB data structures

   rqb_dev	RQB device descriptor
   rqb_unit	RQB unit list
   rqb_reg	RQB register list
   rqb_mod	RQB modifier list
*/

MSC rqb_ctx = { 1 };

DIB rqb_dib = { IOBA_RQB, IOLN_RQB, &rq_rd, &rq_wr,
		1, IVCL (RQ), 0, { &rq_inta } };

UNIT rqb_unit[] = {
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_tmrsvc, UNIT_DIS, 0) },
	{ UDATA (&rq_quesvc, UNIT_DIS, 0) }  };

REG rqb_reg[] = {
	{ GRDATA (SA, rqb_ctx.sa, DEV_RDX, 16, 0) },
	{ GRDATA (SAW, rqb_ctx.saw, DEV_RDX, 16, 0) },
	{ GRDATA (S1DAT, rqb_ctx.s1dat, DEV_RDX, 16, 0) },
	{ GRDATA (COMM, rqb_ctx.comm, DEV_RDX, 22, 0) },
	{ GRDATA (CQBA, rqb_ctx.cq.ba, DEV_RDX, 22, 0) },
	{ GRDATA (CQLNT, rqb_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ },
	{ GRDATA (CQIDX, rqb_ctx.cq.idx, DEV_RDX, 8, 2) },
	{ GRDATA (RQBA, rqb_ctx.rq.ba, DEV_RDX, 22, 0) },
	{ GRDATA (RQLNT, rqb_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ },
	{ GRDATA (RQIDX, rqb_ctx.rq.idx, DEV_RDX, 8, 2) },
	{ DRDATA (FREE, rqb_ctx.freq, 5) },
	{ DRDATA (RESP, rqb_ctx.rspq, 5) },
	{ DRDATA (PBSY, rqb_ctx.pbsy, 5) },
	{ GRDATA (CFLGS, rqb_ctx.cflgs, DEV_RDX, 16, 0) },
	{ GRDATA (CSTA, rqb_ctx.csta, DEV_RDX, 4, 0) },
	{ GRDATA (PERR, rqb_ctx.perr, DEV_RDX, 9, 0) },
	{ DRDATA (CRED, rqb_ctx.credits, 5) },
	{ DRDATA (HAT, rqb_ctx.hat, 17) },
	{ DRDATA (HTMO, rqb_ctx.htmo, 17) },
	{ FLDATA (PRGI, rqb_ctx.prgi, 0), REG_HIDDEN },
	{ FLDATA (PIP, rqb_ctx.pip, 0), REG_HIDDEN },
	{ FLDATA (INT, rqb_ctx.irq, 0) },
	{ BRDATA (PKTS, rqb_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) },
	{ URDATA (CPKT, rqb_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) },
	{ URDATA (PKTQ, rqb_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) },
	{ URDATA (UFLG, rqb_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) },
	{ URDATA (CAPAC, rqb_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) },
	{ GRDATA (DEVADDR, rqb_dib.ba, DEV_RDX, 32, 0), REG_HRO },
	{ GRDATA (DEVVEC, rqb_dib.vec, DEV_RDX, 16, 0), REG_HRO },
	{ NULL }  };

DEVICE rqb_dev = {
	"RQB", rqb_unit, rqb_reg, rq_mod,
	RQ_NUMDR + 2, DEV_RDX, 31, RQ_AINC, DEV_RDX, RQ_WID,
	NULL, NULL, &rq_reset,
	&rq_boot, &rq_attach, &rq_detach,
	&rqb_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG };

/* RQC data structures

   rqc_dev	RQC device descriptor
   rqc_unit	RQC unit list
   rqc_reg	RQC register list
   rqc_mod	RQC modifier list
*/

MSC rqc_ctx = { 2 };

DIB rqc_dib = { IOBA_RQC, IOLN_RQC, &rq_rd, &rq_wr,
		1, IVCL (RQ), 0, { &rq_inta } };

UNIT rqc_unit[] = {
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_tmrsvc, UNIT_DIS, 0) },
	{ UDATA (&rq_quesvc, UNIT_DIS, 0) }  };

REG rqc_reg[] = {
	{ GRDATA (SA, rqc_ctx.sa, DEV_RDX, 16, 0) },
	{ GRDATA (SAW, rqc_ctx.saw, DEV_RDX, 16, 0) },
	{ GRDATA (S1DAT, rqc_ctx.s1dat, DEV_RDX, 16, 0) },
	{ GRDATA (COMM, rqc_ctx.comm, DEV_RDX, 22, 0) },
	{ GRDATA (CQBA, rqc_ctx.cq.ba, DEV_RDX, 22, 0) },
	{ GRDATA (CQLNT, rqc_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ },
	{ GRDATA (CQIDX, rqc_ctx.cq.idx, DEV_RDX, 8, 2) },
	{ GRDATA (RQBA, rqc_ctx.rq.ba, DEV_RDX, 22, 0) },
	{ GRDATA (RQLNT, rqc_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ },
	{ GRDATA (RQIDX, rqc_ctx.rq.idx, DEV_RDX, 8, 2) },
	{ DRDATA (FREE, rqc_ctx.freq, 5) },
	{ DRDATA (RESP, rqc_ctx.rspq, 5) },
	{ DRDATA (PBSY, rqc_ctx.pbsy, 5) },
	{ GRDATA (CFLGS, rqc_ctx.cflgs, DEV_RDX, 16, 0) },
	{ GRDATA (CSTA, rqc_ctx.csta, DEV_RDX, 4, 0) },
	{ GRDATA (PERR, rqc_ctx.perr, DEV_RDX, 9, 0) },
	{ DRDATA (CRED, rqc_ctx.credits, 5) },
	{ DRDATA (HAT, rqc_ctx.hat, 17) },
	{ DRDATA (HTMO, rqc_ctx.htmo, 17) },
	{ FLDATA (PRGI, rqc_ctx.prgi, 0), REG_HIDDEN },
	{ FLDATA (PIP, rqc_ctx.pip, 0), REG_HIDDEN },
	{ FLDATA (INT, rqc_ctx.irq, 0) },
	{ BRDATA (PKTS, rqc_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) },
	{ URDATA (CPKT, rqc_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) },
	{ URDATA (PKTQ, rqc_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) },
	{ URDATA (UFLG, rqc_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) },
	{ URDATA (CAPAC, rqc_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) },
	{ GRDATA (DEVADDR, rqc_dib.ba, DEV_RDX, 32, 0), REG_HRO },
	{ GRDATA (DEVVEC, rqc_dib.vec, DEV_RDX, 16, 0), REG_HRO },
	{ NULL }  };

DEVICE rqc_dev = {
	"RQC", rqc_unit, rqc_reg, rq_mod,
	RQ_NUMDR + 2, DEV_RDX, 31, RQ_AINC, DEV_RDX, RQ_WID,
	NULL, NULL, &rq_reset,
	&rq_boot, &rq_attach, &rq_detach,
	&rqc_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG };

/* RQD data structures

   rqd_dev	RQ device descriptor
   rqd_unit	RQ unit list
   rqd_reg	RQ register list
   rqd_mod	RQ modifier list
*/

MSC rqd_ctx = { 3 };

DIB rqd_dib = { IOBA_RQD, IOLN_RQD, &rq_rd, &rq_wr,
		1, IVCL (RQ), 0, { &rq_inta } };

UNIT rqd_unit[] = {
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
		(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) },
	{ UDATA (&rq_tmrsvc, UNIT_DIS, 0) },
	{ UDATA (&rq_quesvc, UNIT_DIS, 0) }  };

REG rqd_reg[] = {
	{ GRDATA (SA, rqd_ctx.sa, DEV_RDX, 16, 0) },
	{ GRDATA (SAW, rqd_ctx.saw, DEV_RDX, 16, 0) },
	{ GRDATA (S1DAT, rqd_ctx.s1dat, DEV_RDX, 16, 0) },
	{ GRDATA (COMM, rqd_ctx.comm, DEV_RDX, 22, 0) },
	{ GRDATA (CQBA, rqd_ctx.cq.ba, DEV_RDX, 22, 0) },
	{ GRDATA (CQLNT, rqd_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ },
	{ GRDATA (CQIDX, rqd_ctx.cq.idx, DEV_RDX, 8, 2) },
	{ GRDATA (RQBA, rqd_ctx.rq.ba, DEV_RDX, 22, 0) },
	{ GRDATA (RQLNT, rqd_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ },
	{ GRDATA (RQIDX, rqd_ctx.rq.idx, DEV_RDX, 8, 2) },
	{ DRDATA (FREE, rqd_ctx.freq, 5) },
	{ DRDATA (RESP, rqd_ctx.rspq, 5) },
	{ DRDATA (PBSY, rqd_ctx.pbsy, 5) },
	{ GRDATA (CFLGS, rqd_ctx.cflgs, DEV_RDX, 16, 0) },
	{ GRDATA (CSTA, rqd_ctx.csta, DEV_RDX, 4, 0) },
	{ GRDATA (PERR, rqd_ctx.perr, DEV_RDX, 9, 0) },
	{ DRDATA (CRED, rqd_ctx.credits, 5) },
	{ DRDATA (HAT, rqd_ctx.hat, 17) },
	{ DRDATA (HTMO, rqd_ctx.htmo, 17) },
	{ FLDATA (PRGI, rqd_ctx.prgi, 0), REG_HIDDEN },
	{ FLDATA (PIP, rqd_ctx.pip, 0), REG_HIDDEN },
	{ FLDATA (INT, rqd_ctx.irq, 0) },
	{ BRDATA (PKTS, rqd_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) },
	{ URDATA (CPKT, rqd_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) },
	{ URDATA (PKTQ, rqd_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) },
	{ URDATA (UFLG, rqd_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) },
	{ URDATA (CAPAC, rqd_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) },
	{ GRDATA (DEVADDR, rqd_dib.ba, DEV_RDX, 32, 0), REG_HRO },
	{ GRDATA (DEVVEC, rqd_dib.vec, DEV_RDX, 16, 0), REG_HRO },
	{ NULL }  };

DEVICE rqd_dev = {
	"RQD", rqd_unit, rqd_reg, rq_mod,
	RQ_NUMDR + 2, DEV_RDX, 31, RQ_AINC, DEV_RDX, RQ_WID,
	NULL, NULL, &rq_reset,
	&rq_boot, &rq_attach, &rq_detach,
	&rqd_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG };

static DEVICE *rq_devmap[RQ_NUMCT] = {
	&rq_dev, &rqb_dev, &rqc_dev, &rqd_dev  };

static MSC *rq_ctxmap[RQ_NUMCT] = {
	&rq_ctx, &rqb_ctx, &rqc_ctx, &rqd_ctx  };

/* I/O dispatch routine, I/O addresses 17772150 - 17772152

   base + 0	IP	read/write
   base + 2	SA	read/write
*/

t_stat rq_rd (int32 *data, int32 PA, int32 access)
{
int32 cidx = rq_map_pa ((uint32) PA);
MSC *cp = rq_ctxmap[cidx];
DEVICE *dptr = rq_devmap[cidx];

if (cidx < 0) return SCPE_IERR;
switch ((PA >> 1) & 01) {				/* decode PA<1> */
case 0:							/* IP */
	*data = 0;					/* reads zero */
	if (cp->csta == CST_S3_PPB) rq_step4 (cp);	/* waiting for poll? */
	else if (cp->csta == CST_UP) {			/* if up */
	    cp->pip = 1;				/* poll host */
	    sim_activate (dptr->units + RQ_QUEUE, rq_qtime);  }
	break;
case 1:							/* SA */
	*data = cp->sa;
	break;  }
return SCPE_OK;
}

t_stat rq_wr (int32 data, int32 PA, int32 access)
{
int32 cidx = rq_map_pa ((uint32) PA);
MSC *cp = rq_ctxmap[cidx];
DEVICE *dptr = rq_devmap[cidx];

if (cidx < 0) return SCPE_IERR;
switch ((PA >> 1) & 01) {				/* decode PA<1> */
case 0:							/* IP */
	rq_reset (rq_devmap[cidx]);			/* init device */
	if (DEBUG_PRD (dptr)) fprintf (sim_deb,
	    ">>RQ%c: initialization started\n", 'A' + cp->cnum);
	break;
case 1:							/* SA */
	cp->saw = data;
	if (cp->csta < CST_S4)				/* stages 1-3 */
	    sim_activate (dptr->units + RQ_QUEUE, rq_itime);
	else if (cp->csta == CST_S4)			/* stage 4 (fast) */
	    sim_activate (dptr->units + RQ_QUEUE, rq_itime4);
	break;  }
return SCPE_OK;
}

/* Map physical address to device context */

int32 rq_map_pa (uint32 pa)
{
int32 i;
DEVICE *dptr;
DIB *dibp;

for (i = 0; i < RQ_NUMCT; i++) {			/* loop thru ctrls */
	dptr = rq_devmap[i];				/* get device */
	dibp = (DIB *) dptr->ctxt;			/* get DIB */
	if ((pa >= dibp->ba) &&				/* in range? */
	    (pa < (dibp->ba + dibp->lnt)))
	    return i;  }				/* return ctrl idx */
return -1;
}

/* Transition to step 4 - init communications region */

t_bool rq_step4 (MSC *cp)
{
int32 i, lnt;
uint32 base;
uint16 zero[SA_COMM_MAX >> 1];

cp->rq.ioff = SA_COMM_RI;				/* set intr offset */
cp->rq.ba = cp->comm;					/* set rsp q base */
cp->rq.lnt = SA_S1H_RQ (cp->s1dat) << 2;		/* get resp q len */
cp->cq.ioff = SA_COMM_CI;				/* set intr offset */
cp->cq.ba = cp->comm + cp->rq.lnt;			/* set cmd q base */
cp->cq.lnt = SA_S1H_CQ (cp->s1dat) << 2;		/* get cmd q len */
cp->cq.idx = cp->rq.idx = 0;				/* clear q idx's */
if (cp->prgi) base = cp->comm + SA_COMM_QQ;
else base = cp->comm + SA_COMM_CI;
lnt = cp->comm + cp->cq.lnt + cp->rq.lnt - base;	/* comm lnt */
if (lnt > SA_COMM_MAX) lnt = SA_COMM_MAX;		/* paranoia */
for (i = 0; i < (lnt >> 1); i++) zero[i] = 0;		/* clr buffer */
if (Map_WriteW (base, lnt, zero, MAP))			/* zero comm area */
	return rq_fatal (cp, PE_QWE);			/* error? */
cp->sa = SA_S4 | (RQ_UQPM << SA_S4C_V_MOD) |		/* send step 4 */
	(RQ_SVER << SA_S4C_V_VER);
cp->csta = CST_S4;					/* set step 4 */
rq_init_int (cp);					/* 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 rq_quesvc (UNIT *uptr)
{
int32 i, cnid;
int32 pkt = 0;
UNIT *nuptr;
MSC *cp = rq_ctxmap[uptr->cnum];
DEVICE *dptr = rq_devmap[uptr->cnum];
DIB *dibp = (DIB *) dptr->ctxt;

if (cp->csta < CST_UP) {				/* still init? */
    switch (cp->csta) {					/* controller state? */
    case CST_S1:					/* need S1 reply */
	if (cp->saw & SA_S1H_VL) {			/* valid? */
	    if (cp->saw & SA_S1H_WR) {			/* wrap? */
		cp->sa = cp->saw;			/* echo data */
		cp->csta = CST_S1_WR;  }		/* endless loop */
	else {
	    cp->s1dat = cp->saw;			/* save data */
	    dibp->vec = (cp->s1dat & SA_S1H_VEC) << 2;	/* get vector */
	    if (dibp->vec) dibp->vec = dibp->vec + VEC_Q; /* if nz, bias */
	    cp->sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (cp->s1dat);
	    cp->csta = CST_S2;				/* now in step 2 */
	    rq_init_int (cp);  }			/* intr if req */
	    }						/* end if valid */
	break;
    case CST_S1_WR:					/* wrap mode */
	cp->sa = cp->saw;				/* echo data */
	break;
    case CST_S2:					/* need S2 reply */
	cp->comm = cp->saw & SA_S2H_CLO;		/* get low addr */
	cp->prgi = cp->saw & SA_S2H_PI;			/* get purge int */
	cp->sa = SA_S3 | SA_S3C_EC (cp->s1dat);
	cp->csta = CST_S3;				/* now in step 3 */
	rq_init_int (cp);				/* intr if req */
	break;
    case CST_S3:					/* need S3 reply */
	cp->comm = ((cp->saw & SA_S3H_CHI) << 16) | cp->comm;
	if (cp->saw & SA_S3H_PP) {			/* purge/poll test? */
	    cp->sa = 0;					/* put 0 */
	    cp->csta = CST_S3_PPA;  }			/* wait for 0 write */
	else rq_step4 (cp);				/* send step 4 */
	break;
    case CST_S3_PPA:					/* need purge test */
	if (cp->saw) rq_fatal (cp, PE_PPF);		/* data not zero? */
	else cp->csta = CST_S3_PPB;			/* wait for poll */
	break;
    case CST_S4:					/* need S4 reply */
	if (cp->saw & SA_S4H_GO) {			/* go set? */
	    if (DEBUG_PRD (dptr)) fprintf (sim_deb,
		">>RQ%c: initialization complete\n", 'A' + cp->cnum);
	    cp->csta = CST_UP;				/* we're up */
	    cp->sa = 0;					/* clear SA */
	    sim_activate (dptr->units + RQ_TIMER, tmr_poll * clk_tps);
	    if ((cp->saw & SA_S4H_LF) && cp->perr) rq_plf (cp, cp->perr);
	    cp->perr = 0;  }
	break;  }					/* end switch */			
    return SCPE_OK;  }					/* end if */

for (i = 0; i < RQ_NUMDR; i++) {			/* chk unit q's */
	nuptr = dptr->units + i;			/* ptr to unit */
	if (nuptr->cpkt || (nuptr->pktq == 0)) continue;
	pkt = rq_deqh (cp, &nuptr->pktq);		/* get top of q */
	if (!rq_mscp (cp, pkt, FALSE)) return SCPE_OK;  }	/* process */
if ((pkt == 0) && cp->pip) {				/* polling? */
    if (!rq_getpkt (cp, &pkt)) return SCPE_OK;		/* get host pkt */
    if (pkt) {						/* got one? */
        if (DEBUG_PRD (dptr)) {
		fprintf (sim_deb, ">>RQ%c: cmd=%04X, mod=%04X, unit=%d, ",
		'A' + cp->cnum, cp->pak[pkt].d[CMD_OPC],
		cp->pak[pkt].d[CMD_MOD], cp->pak[pkt].d[CMD_UN]);
	    fprintf (sim_deb, "bc=%04X%04X, ma=%04X%04X, lbn=%04X%04X\n",
		cp->pak[pkt].d[RW_BCH], cp->pak[pkt].d[RW_BCL],
		cp->pak[pkt].d[RW_BAH], cp->pak[pkt].d[RW_BAL],
		cp->pak[pkt].d[RW_LBNH], cp->pak[pkt].d[RW_LBNL]);  }
	if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ)	/* seq packet? */
	    return rq_fatal (cp, PE_PIE);		/* no, term thread */
	cnid = GETP (pkt, UQ_HCTC, CID);		/* get conn ID */
	if (cnid == UQ_CID_MSCP) {			/* MSCP packet? */
	    if (!rq_mscp (cp, pkt, TRUE)) return SCPE_OK;  } /* proc, q non-seq */
	else if (cnid == UQ_CID_DUP) {			/* DUP packet? */
	    rq_putr (cp, pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ);
	    if (!rq_putpkt (cp, pkt, TRUE)) return SCPE_OK;  } /* ill cmd */
 	else return rq_fatal (cp, PE_ICI);		/* no, term thread */
	}						/* end if pkt */
    else cp->pip = 0;					/* discontinue poll */
    }							/* end if pip */
if (cp->rspq) {						/* resp q? */
    pkt = rq_deqh (cp, &cp->rspq);			/* get top of q */
    if (!rq_putpkt (cp, pkt, FALSE)) return SCPE_OK;	/* send to hst */
    }							/* end if resp q */
if (pkt) sim_activate (uptr, rq_qtime);			/* more to do? */
return SCPE_OK;						/* done */
}

/* Clock service (roughly once per second) */

t_stat rq_tmrsvc (UNIT *uptr)
{
int32 i;
UNIT *nuptr;
MSC *cp = rq_ctxmap[uptr->cnum];
DEVICE *dptr = rq_devmap[uptr->cnum];

sim_activate (uptr, tmr_poll * clk_tps);		/* reactivate */
for (i = 0; i < RQ_NUMDR; i++) {			/* poll */
	nuptr = dptr->units + i;
	if ((nuptr->flags & UNIT_ATP) &&		/* ATN pending? */
	    (nuptr->flags & UNIT_ATT) &&		/* still online? */
	    (cp->cflgs & CF_ATN)) {			/* wanted? */
	    if (!rq_una (cp, i)) return SCPE_OK;  }
	nuptr->flags = nuptr->flags & ~UNIT_ATP;  }
if ((cp->hat > 0) && (--cp->hat == 0))			/* host timeout? */
	rq_fatal (cp, PE_HAT);				/* fatal err */	
return SCPE_OK;
}

/* MSCP packet handling */

t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q)
{
uint32 sts, cmd = GETP (pkt, CMD_OPC, OPC);

switch (cmd) {
case OP_ABO:						/* abort */
	return rq_abo (cp, pkt, q);
case OP_AVL:						/* avail */
	return rq_avl (cp, pkt, q);
case OP_FMT:						/* format */
	return rq_fmt (cp, pkt, q);
case OP_GCS:						/* get cmd status */
	return rq_gcs (cp, pkt, q);
case OP_GUS:						/* get unit status */
	return rq_gus (cp, pkt, q);
case OP_ONL:						/* online */
	return rq_onl (cp, pkt, q);
case OP_SCC:						/* set ctrl char */
	return rq_scc (cp, pkt, q);
case OP_SUC:						/* set unit char */
	return rq_suc (cp, pkt, q);
case OP_ACC:						/* access */
case OP_CMP:						/* compare */
case OP_ERS:						/* erase */
case OP_RD:						/* read */
case OP_WR:						/* write */
	return rq_rw (cp, pkt, q);
case OP_CCD:						/* nops */
case OP_DAP:
case OP_FLU:
	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;  }
rq_putr (cp, pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Abort a command - 1st parameter is ref # of cmd to abort */

t_bool rq_abo (MSC *cp, int32 pkt, t_bool q)
{
uint32 lu = cp->pak[pkt].d[CMD_UN];			/* unit # */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* opcode */
uint32 ref = GETP32 (pkt, ABO_REFL);			/* cmd ref # */
int32 tpkt, prv;
UNIT *uptr;
DEVICE *dptr = rq_devmap[cp->cnum];

tpkt = 0;						/* set no mtch */
if (uptr = rq_getucb (cp, 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 (dptr->units + RQ_QUEUE, rq_qtime);  }
	else if (uptr->pktq &&				/* head of q? */
	    (GETP32 (uptr->pktq, CMD_REFL) == ref)) {	/* match ref? */
	    tpkt = uptr->pktq;				/* save match */
	    uptr->pktq = cp->pak[tpkt].link;  }		/* unlink */
	else if (prv = uptr->pktq) {			/* srch pkt q */
	    while (tpkt = cp->pak[prv].link) {		/* walk list */
		if (GETP32 (tpkt, RSP_REFL) == ref) {	/* match? unlink */
		    cp->pak[prv].link = cp->pak[tpkt].link;
		    break;  }  }  }
	if (tpkt) {					/* found target? */
	    uint32 tcmd = GETP (tpkt, CMD_OPC, OPC);	/* get opcode */
	    rq_putr (cp, tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ);
	    if (!rq_putpkt (cp, tpkt, TRUE)) return ERR;  }
	}						/* end if unit */
rq_putr (cp, pkt, cmd | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Unit available - set unit status to available - defer if q'd cmds */

t_bool rq_avl (MSC *cp, int32 pkt, t_bool q)
{
uint32 lu = cp->pak[pkt].d[CMD_UN];			/* unit # */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* opcode */
uint32 sts;
UNIT *uptr;

if (uptr = rq_getucb (cp, lu)) {			/* unit exist? */
	if (q && uptr->cpkt) {				/* need to queue? */
	    rq_enqt (cp, &uptr->pktq, pkt);		/* do later */
	    return OK;  }
	uptr->flags = uptr->flags & ~UNIT_ONL;		/* not online */
	uptr->uf = 0;					/* clr flags */
	sts = ST_SUC;  }				/* success */
else sts = ST_OFL;					/* offline */
rq_putr (cp, pkt, cmd | OP_END, 0, sts, AVL_LNT, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Get command status - only interested in active xfr cmd */

t_bool rq_gcs (MSC *cp, int32 pkt, t_bool q)
{
uint32 lu = cp->pak[pkt].d[CMD_UN];			/* unit # */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* opcode */
uint32 ref = GETP32 (pkt, GCS_REFL);			/* ref # */
int32 tpkt;
UNIT *uptr;

if ((uptr = rq_getucb (cp, lu)) && 			/* valid lu? */
    (tpkt = uptr->cpkt) &&				/* queued pkt? */
    (GETP32 (tpkt, CMD_REFL) == ref) &&			/* match ref? */
    (GETP (tpkt, CMD_OPC, OPC) >= OP_ACC)) {		/* rd/wr cmd? */
    cp->pak[pkt].d[GCS_STSL] = cp->pak[tpkt].d[RW_WBCL];
    cp->pak[pkt].d[GCS_STSH] = cp->pak[tpkt].d[RW_WBCH];  }
else {
    cp->pak[pkt].d[GCS_STSL] = 0;			/* return 0 */
    cp->pak[pkt].d[GCS_STSH] = 0;  }
rq_putr (cp, pkt, cmd | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Get unit status */

t_bool rq_gus (MSC *cp, int32 pkt, t_bool q)
{
uint32 lu = cp->pak[pkt].d[CMD_UN];			/* unit # */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* opcode */
uint32 dtyp, sts, rbpar;
UNIT *uptr;

if (cp->pak[pkt].d[CMD_MOD] & MD_NXU) {			/* next unit? */
	if (lu >= (cp->ubase + RQ_NUMDR)) {		/* end of range? */
	    lu = 0;					/* reset to 0 */
	    cp->pak[pkt].d[RSP_UN] = lu;  }  }
if (uptr = rq_getucb (cp, 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) sts = ST_SUC;	/* online */
	else sts = ST_AVL;				/* avail */
	rq_putr_unit (cp, pkt, uptr, lu, FALSE);	/* fill unit fields */
	dtyp = GET_DTYPE (uptr->flags);			/* get drive type */
	if (drv_tab[dtyp].rcts) rbpar = 1;		/* ctrl bad blk? */
	else rbpar = 0;					/* fill geom, bblk */
	cp->pak[pkt].d[GUS_TRK] = drv_tab[dtyp].sect;
	cp->pak[pkt].d[GUS_GRP] = drv_tab[dtyp].tpg;
	cp->pak[pkt].d[GUS_CYL] = drv_tab[dtyp].gpc;
	cp->pak[pkt].d[GUS_UVER] = 0;
	cp->pak[pkt].d[GUS_RCTS] = drv_tab[dtyp].rcts;
	cp->pak[pkt].d[GUS_RBSC] =
	    (rbpar << GUS_RB_V_RBNS) | (rbpar << GUS_RB_V_RCTC);  }
else sts = ST_OFL;					/* offline */
cp->pak[pkt].d[GUS_SHUN] = lu;				/* shadowing */
cp->pak[pkt].d[GUS_SHST] = 0;
rq_putr (cp, pkt, cmd | OP_END, 0, sts, GUS_LNT_D, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Unit online - defer if q'd commands */

t_bool rq_onl (MSC *cp, int32 pkt, t_bool q)
{
uint32 lu = cp->pak[pkt].d[CMD_UN];			/* unit # */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* opcode */
uint32 sts;
UNIT *uptr;

if (uptr = rq_getucb (cp, lu)) {			/* unit exist? */
	if (q && uptr->cpkt) {				/* need to queue? */
	    rq_enqt (cp, &uptr->pktq, pkt);		/* do later */
	    return OK;  }
	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 {						/* mark online */
	    sts = ST_SUC;
	    uptr->flags = uptr->flags | UNIT_ONL;
	    rq_setf_unit (cp, pkt, uptr);  }		/* hack flags */
	rq_putr_unit (cp, pkt, uptr, lu, TRUE);  }	/* set fields */
else sts = ST_OFL;					/* offline */
cp->pak[pkt].d[ONL_SHUN] = lu;				/* shadowing */
cp->pak[pkt].d[ONL_SHST] = 0;
rq_putr (cp, pkt, cmd | OP_END, 0, sts, ONL_LNT, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Set controller characteristics */

t_bool rq_scc (MSC *cp, int32 pkt, t_bool q)
{
int32 sts, cmd;

if (cp->pak[pkt].d[SCC_MSV]) {				/* MSCP ver = 0? */
	sts = ST_CMD | I_VRSN;				/* no, lose */
	cmd = 0;  }
else {	sts = ST_SUC;					/* success */
	cmd = GETP (pkt, CMD_OPC, OPC);			/* get opcode */
	cp->cflgs = (cp->cflgs & CF_RPL) |		/* hack ctrl flgs */
	    cp->pak[pkt].d[SCC_CFL];
	if (cp->htmo = cp->pak[pkt].d[SCC_TMO])		/* set timeout */
	    cp->htmo = cp->htmo + 2;			/* if nz, round up */
	cp->pak[pkt].d[SCC_CFL] = cp->cflgs;		/* return flags */
	cp->pak[pkt].d[SCC_TMO] = RQ_DCTMO;		/* ctrl timeout */
	cp->pak[pkt].d[SCC_VER] = (RQ_HVER << SCC_VER_V_HVER) |
	    (RQ_SVER << SCC_VER_V_SVER);
	cp->pak[pkt].d[SCC_CIDA] = 0;			/* ctrl ID */
	cp->pak[pkt].d[SCC_CIDB] = 0;
	cp->pak[pkt].d[SCC_CIDC] = 0;
	cp->pak[pkt].d[SCC_CIDD] = (RQ_CLASS << SCC_CIDD_V_CLS) |
	    (RQ_MODEL << SCC_CIDD_V_MOD);
	cp->pak[pkt].d[SCC_MBCL] = 0;			/* max bc */
	cp->pak[pkt].d[SCC_MBCH] = 0;  }
rq_putr (cp, pkt, cmd | OP_END, 0, sts, SCC_LNT, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}
	
/* Set unit characteristics - defer if q'd commands */

t_bool rq_suc (MSC *cp, int32 pkt, t_bool q)
{
uint32 lu = cp->pak[pkt].d[CMD_UN];			/* unit # */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* opcode */
uint32 sts;
UNIT *uptr;

if (uptr = rq_getucb (cp, lu)) {			/* unit exist? */
	if (q && uptr->cpkt) {				/* need to queue? */
	    rq_enqt (cp, &uptr->pktq, pkt);		/* do later */
	    return OK;  }
	if ((uptr->flags & UNIT_ATT) == 0)		/* not attached? */
	    sts = ST_OFL | SB_OFL_NV;			/* offl no vol */
	else {						/* avail or onl */
	    sts = ST_SUC;
	    rq_setf_unit (cp, pkt, uptr);  }		/* hack flags */
	rq_putr_unit (cp, pkt, uptr, lu, TRUE);  }	/* set fields */
else sts = ST_OFL;					/* offline */
cp->pak[pkt].d[ONL_SHUN] = lu;				/* shadowing */
cp->pak[pkt].d[ONL_SHST] = 0;
rq_putr (cp, pkt, cmd | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Format command - floppies only */

t_bool rq_fmt (MSC *cp, int32 pkt, t_bool q)
{
uint32 lu = cp->pak[pkt].d[CMD_UN];			/* unit # */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* opcode */
uint32 sts;
UNIT *uptr;

if (uptr = rq_getucb (cp, lu)) {			/* unit exist? */
	if (q && uptr->cpkt) {				/* need to queue? */
	    rq_enqt (cp, &uptr->pktq, pkt);		/* do later */
	    return OK;  }
	if (GET_DTYPE (uptr->flags) != RX33_DTYPE)	/* RX33? */
	    sts = ST_CMD | I_OPCD;			/* no, err */
	else if ((cp->pak[pkt].d[FMT_IH] & 0100000) == 0) /* magic bit set? */
	    sts = ST_CMD | I_FMTI;			/* no, err */
	else if ((uptr->flags & UNIT_ATT) == 0)		/* offline? */
	    sts = ST_OFL | SB_OFL_NV;			/* no vol */
	else if (uptr->flags & UNIT_ONL) {		/* online? */
	    uptr->flags = uptr->flags & ~UNIT_ONL;
	    uptr->uf = 0;				/* clear flags */
	    sts = ST_AVL | SB_AVL_INU;  }		/* avail, in use */
	else if (RQ_WPH (uptr))				/* write prot? */
	    sts = ST_WPR | SB_WPR_HW;			/* can't fmt */
	else sts = ST_SUC;				/*** for now ***/
	}
else sts = ST_OFL;					/* offline */
rq_putr (cp, pkt, cmd | OP_END, 0, sts, FMT_LNT, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Data transfer commands */

t_bool rq_rw (MSC *cp, int32 pkt, t_bool q)
{
uint32 lu = cp->pak[pkt].d[CMD_UN];			/* unit # */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* opcode */
uint32 sts;
UNIT *uptr;

if (uptr = rq_getucb (cp, lu)) {			/* unit exist? */
	if (q && uptr->cpkt) {				/* need to queue? */
	    rq_enqt (cp, &uptr->pktq, pkt);		/* do later */
	    return OK;  }
	sts = rq_rw_valid (cp, pkt, uptr, cmd);		/* validity checks */
	if (sts == 0) {					/* ok? */
	    uptr->cpkt = pkt;				/* op in progress */
	    cp->pak[pkt].d[RW_WBAL] = cp->pak[pkt].d[RW_BAL];
	    cp->pak[pkt].d[RW_WBAH] = cp->pak[pkt].d[RW_BAH];
	    cp->pak[pkt].d[RW_WBCL] = cp->pak[pkt].d[RW_BCL];
	    cp->pak[pkt].d[RW_WBCH] = cp->pak[pkt].d[RW_BCH];
	    cp->pak[pkt].d[RW_WBLL] = cp->pak[pkt].d[RW_LBNL];
	    cp->pak[pkt].d[RW_WBLH] = cp->pak[pkt].d[RW_LBNH];
	    sim_activate (uptr, rq_xtime);		/* activate */
	    return OK;  }  }				/* done */
else sts = ST_OFL;					/* offline */
cp->pak[pkt].d[RW_BCL] = cp->pak[pkt].d[RW_BCH] = 0;	/* bad packet */
rq_putr (cp, pkt, cmd | OP_END, 0, sts, RW_LNT_D, UQ_TYP_SEQ);
return rq_putpkt (cp, pkt, TRUE);
}

/* Validity checks */

int32 rq_rw_valid (MSC *cp, int32 pkt, UNIT *uptr, uint32 cmd)
{
uint32 dtyp = GET_DTYPE (uptr->flags);			/* get drive type */
uint32 lbn = GETP32 (pkt, RW_LBNL);			/* get lbn */
uint32 bc = GETP32 (pkt, RW_BCL);			/* get byte cnt */
uint32 maxlbn = uptr->capac / RQ_NUMBY;			/* get max lbn */
/* uint32 maxlbn = drv_tab[dtyp].lbn;			/* get max lbn */

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 ((cmd != OP_ACC) && (cmd != OP_ERS) &&		/* 'real' xfer */
    (cp->pak[pkt].d[RW_BAL] & 1))			/* odd address? */
	return (ST_HST | SB_HST_OA);			/* host buf odd */
if (bc & 1) return (ST_HST | SB_HST_OC);		/* odd byte cnt? */
if (bc & 0xF0000000) return (ST_CMD | I_BCNT);		/* 'reasonable' bc? */
/* if (lbn & 0xF0000000) return (ST_CMD | I_LBN);	/* 'reasonable' lbn? */
if (lbn >= maxlbn) {					/* accessing RCT? */
	if (lbn >= (maxlbn + drv_tab[dtyp].rcts))	/* beyond copy 1? */
	    return (ST_CMD | I_LBN);			/* lbn err */
	if (bc != RQ_NUMBY) return (ST_CMD | I_BCNT);  }/* bc must be 512 */
else if ((lbn + ((bc + (RQ_NUMBY - 1)) / RQ_NUMBY)) > maxlbn)
	return (ST_CMD | I_BCNT);			/* spiral to RCT */
if ((cmd == OP_WR) || (cmd == OP_ERS)) {		/* write op? */
	if (lbn >= maxlbn)				/* accessing RCT? */
	    return (ST_CMD | I_LBN);			/* lbn err */
	if (uptr->uf & UF_WPS)				/* swre wlk? */
	    return (ST_WPR | SB_WPR_SW);
	if (RQ_WPH (uptr))				/* hwre wlk? */
	    return (ST_WPR | SB_WPR_HW);  }
return 0;						/* success! */
}

/* Unit service for data transfer commands */

t_stat rq_svc (UNIT *uptr)
{
MSC *cp = rq_ctxmap[uptr->cnum];

uint32 i, t, tbc, abc, wwc;
uint32 err = 0;
int32 pkt = uptr->cpkt;					/* get packet */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* get cmd */
uint32 ba = GETP32 (pkt, RW_WBAL);			/* buf addr */
uint32 bc = GETP32 (pkt, RW_WBCL);			/* byte count */
uint32 bl = GETP32 (pkt, RW_WBLL);			/* block addr */
t_addr da = ((t_addr) bl) * RQ_NUMBY;			/* disk addr */

if ((cp == NULL) || (pkt == 0)) return STOP_RQ;		/* what??? */
tbc = (bc > RQ_MAXFR)? RQ_MAXFR: bc;			/* trim cnt to max */

if ((uptr->flags & UNIT_ATT) == 0) {			/* not attached? */
	rq_rw_end (cp, uptr, 0, ST_OFL | SB_OFL_NV);	/* offl no vol */
	return SCPE_OK;  }
if (bc == 0) {						/* no xfer? */
	rq_rw_end (cp, uptr, 0, ST_SUC);		/* ok by me... */
	return SCPE_OK;  }

if ((cmd == OP_ERS) || (cmd == OP_WR)) {		/* write op? */
	if (RQ_WPH (uptr)) {
	    rq_rw_end (cp, uptr, 0, ST_WPR | SB_WPR_HW);
	    return SCPE_OK;  }
	if (uptr->uf & UF_WPS) {
	    rq_rw_end (cp, uptr, 0, ST_WPR | SB_WPR_SW);
	    return SCPE_OK;  }  }

if (cmd == OP_ERS) {					/* erase? */
	wwc = ((tbc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1;
	for (i = 0; i < wwc; i++) rqxb[i] = 0;		/* clr buf */
	err = sim_fseek (uptr->fileref, da, SEEK_SET);	/* set pos */
	if (!err) sim_fwrite (rqxb, sizeof (int16), wwc, uptr->fileref);
	err = ferror (uptr->fileref);  }		/* end if erase */

else if (cmd == OP_WR) {				/* write? */
	t = Map_ReadW (ba, tbc, rqxb, MAP);		/* fetch buffer */
	if (abc = tbc - t) {				/* any xfer? */
	    wwc = ((abc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1;
	    for (i = (abc >> 1); i < wwc; i++) rqxb[i] = 0;
	    err = sim_fseek (uptr->fileref, da, SEEK_SET);
	    if (!err) sim_fwrite (rqxb, sizeof (int16), wwc, uptr->fileref);
	    err = ferror (uptr->fileref);  }
	if (t) {					/* nxm? */
	    PUTP32 (pkt, RW_WBCL, bc - abc);		/* adj bc */
	    PUTP32 (pkt, RW_WBAL, ba + abc);		/* adj ba */
	    if (rq_hbe (cp, uptr))			/* post err log */
		rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM);	
	    return SCPE_OK;  }  }			/* end else wr */

else {	err = sim_fseek (uptr->fileref, da, SEEK_SET);	/* set pos */
	if (!err) {
	    i = sim_fread (rqxb, sizeof (int16), tbc >> 1, uptr->fileref);
	    for ( ; i < (tbc >> 1); i++) rqxb[i] = 0;	/* fill */
	    err = ferror (uptr->fileref);  }
	if ((cmd == OP_RD) && !err) {			/* read? */
	    if (t = Map_WriteW (ba, tbc, rqxb, MAP)) {	/* store, nxm? */
		PUTP32 (pkt, RW_WBCL, bc - (tbc - t));	/* adj bc */
		PUTP32 (pkt, RW_WBAL, ba + (tbc - t));	/* adj ba */
		if (rq_hbe (cp, uptr))			/* post err log */
		    rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM);	
		return SCPE_OK;  }
	    }
	else if ((cmd == OP_CMP) && !err) {		/* compare? */
	    uint8 dby, mby;
	    for (i = 0; i < tbc; i++) {			/* loop */
		if (Map_ReadB (ba + i, 1, &mby, MAP)) {	/* fetch, nxm? */
		    PUTP32 (pkt, RW_WBCL, bc - i);	/* adj bc */
		    PUTP32 (pkt, RW_WBAL, bc - i);	/* adj ba */
		    if (rq_hbe (cp, uptr))		/* post err log */
			rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM);
		    return SCPE_OK;  }
		dby = (rqxb[i >> 1] >> ((i & 1)? 8: 0)) & 0xFF;
		if (mby != dby) {			/* cmp err? */
		    PUTP32 (pkt, RW_WBCL, bc - i);	/* adj bc */
		    rq_rw_end (cp, uptr, 0, ST_CMP);	/* done */
		    return SCPE_OK;  }			/* exit */
		}					/* end for */
	    }						/* end else if */
	}						/* end else read */
if (err != 0) {						/* error? */
	if (rq_dte (cp, uptr, ST_DRV))			/* post err log */
	    rq_rw_end (cp, uptr, EF_LOG, ST_DRV);	/* if ok, report err */
	perror ("RQ I/O error");
	clearerr (uptr->fileref);
	return SCPE_IOERR;  }
ba = ba + tbc;						/* incr bus addr */
bc = bc - tbc;						/* decr byte cnt */
bl = bl + ((tbc + (RQ_NUMBY - 1)) / RQ_NUMBY);		/* incr blk # */
PUTP32 (pkt, RW_WBAL, ba);				/* update pkt */
PUTP32 (pkt, RW_WBCL, bc);
PUTP32 (pkt, RW_WBLL, bl);
if (bc) sim_activate (uptr, rq_xtime);			/* more? resched */
else rq_rw_end (cp, uptr, 0, ST_SUC);			/* done! */
return SCPE_OK;
}

/* Transfer command complete */

t_bool rq_rw_end (MSC *cp, UNIT *uptr, uint32 flg, uint32 sts)
{
int32 pkt = uptr->cpkt;					/* packet */
uint32 cmd = GETP (pkt, CMD_OPC, OPC);			/* get cmd */
uint32 bc = GETP32 (pkt, RW_BCL);			/* init bc */
uint32 wbc = GETP32 (pkt, RW_WBCL);			/* work bc */
DEVICE *dptr = rq_devmap[uptr->cnum];

uptr->cpkt = 0;						/* done */
PUTP32 (pkt, RW_BCL, bc - wbc);				/* bytes processed */
cp->pak[pkt].d[RW_WBAL] = 0;				/* clear temps */
cp->pak[pkt].d[RW_WBAH] = 0;
cp->pak[pkt].d[RW_WBCL] = 0;
cp->pak[pkt].d[RW_WBCH] = 0;
cp->pak[pkt].d[RW_WBLL] = 0;
cp->pak[pkt].d[RW_WBLH] = 0;
rq_putr (cp, pkt, cmd | OP_END, flg, sts, RW_LNT_D, UQ_TYP_SEQ); /* fill pkt */
if (!rq_putpkt (cp, pkt, TRUE)) return ERR;		/* send pkt */
if (uptr->pktq)						/* more to do? */
    sim_activate (dptr->units + RQ_QUEUE, rq_qtime);	/* activate thread */
return OK;
}

/* Data transfer error log packet */

t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err)
{
int32 pkt, tpkt;
uint32 lu, dtyp, lbn, ccyl, csurf, csect, t;

if ((cp->cflgs & CF_THS) == 0) return OK;		/* logging? */
if (!rq_deqf (cp, &pkt)) return ERR;			/* get log pkt */
tpkt = uptr->cpkt;					/* rw pkt */
lu = cp->pak[tpkt].d[CMD_UN];				/* unit # */
lbn = GETP32 (tpkt, RW_WBLL);				/* recent LBN */
dtyp = GET_DTYPE (uptr->flags);				/* drv type */
if (drv_tab[dtyp].flgs & RQDF_SDI) t = 0;		/* SDI? ovhd @ end */
else t = (drv_tab[dtyp].xbn + drv_tab[dtyp].dbn) /	/* ovhd cylinders */
	(drv_tab[dtyp].sect * drv_tab[dtyp].surf);
ccyl = t + (lbn / drv_tab[dtyp].cyl);			/* curr real cyl */
t = lbn % drv_tab[dtyp].cyl;				/* trk relative blk */
csurf = t / drv_tab[dtyp].surf;				/* curr surf */
csect = t % drv_tab[dtyp].surf;				/* curr sect */

cp->pak[pkt].d[ELP_REFL] = cp->pak[tpkt].d[CMD_REFL];	/* copy cmd ref */
cp->pak[pkt].d[ELP_REFH] = cp->pak[tpkt].d[CMD_REFH];
cp->pak[pkt].d[ELP_UN] = lu;				/* copy unit */
cp->pak[pkt].d[ELP_SEQ] = 0;				/* clr seq # */
cp->pak[pkt].d[DTE_CIDA] = 0;				/* ctrl ID */
cp->pak[pkt].d[DTE_CIDB] = 0;
cp->pak[pkt].d[DTE_CIDC] = 0;
cp->pak[pkt].d[DTE_CIDD] = (RQ_CLASS << DTE_CIDD_V_CLS) |
	(RQ_MODEL << DTE_CIDD_V_MOD);
cp->pak[pkt].d[DTE_VER] = (RQ_HVER << DTE_VER_V_HVER) |
	(RQ_SVER << DTE_VER_V_SVER);
cp->pak[pkt].d[DTE_MLUN] = lu;				/* MLUN */
cp->pak[pkt].d[DTE_UIDA] = lu;				/* unit ID */
cp->pak[pkt].d[DTE_UIDB] = 0;
cp->pak[pkt].d[DTE_UIDC] = 0;
cp->pak[pkt].d[DTE_UIDD] = (UID_DISK << DTE_UIDD_V_CLS) |
	(drv_tab[dtyp].mod << DTE_UIDD_V_MOD);
cp->pak[pkt].d[DTE_UVER] = 0;				/* unit versn */
cp->pak[pkt].d[DTE_SCYL] = ccyl;			/* cylinder */
cp->pak[pkt].d[DTE_VSNL] = 01234 + lu;			/* vol ser # */
cp->pak[pkt].d[DTE_VSNH] = 0;
cp->pak[pkt].d[DTE_D1] = 0;
cp->pak[pkt].d[DTE_D2] = csect << DTE_D2_V_SECT;	/* geometry */
cp->pak[pkt].d[DTE_D3] = (ccyl << DTE_D3_V_CYL) |
	(csurf << DTE_D3_V_SURF);
rq_putr (cp, pkt, FM_SDE, LF_SNR, err, DTE_LNT, UQ_TYP_DAT);
return rq_putpkt (cp, pkt, TRUE);
}

/* Host bus error log packet */

t_bool rq_hbe (MSC *cp, UNIT *uptr)
{
int32 pkt, tpkt;

if ((cp->cflgs & CF_THS) == 0) return OK;		/* logging? */
if (!rq_deqf (cp, &pkt)) return ERR;			/* get log pkt */
tpkt = uptr->cpkt;					/* rw pkt */
cp->pak[pkt].d[ELP_REFL] = cp->pak[tpkt].d[CMD_REFL];	/* copy cmd ref */
cp->pak[pkt].d[ELP_REFH] = cp->pak[tpkt].d[CMD_REFH];
cp->pak[pkt].d[ELP_UN] = cp->pak[tpkt].d[CMD_UN];	/* copy unit */
cp->pak[pkt].d[ELP_SEQ] = 0;				/* clr seq # */
cp->pak[pkt].d[HBE_CIDA] = 0;				/* ctrl ID */
cp->pak[pkt].d[HBE_CIDB] = 0;
cp->pak[pkt].d[HBE_CIDC] = 0;
cp->pak[pkt].d[HBE_CIDD] = (RQ_CLASS << DTE_CIDD_V_CLS) |
	(RQ_MODEL << DTE_CIDD_V_MOD);
cp->pak[pkt].d[HBE_VER] = (RQ_HVER << HBE_VER_V_HVER) |	/* versions */
	(RQ_SVER << HBE_VER_V_SVER);
cp->pak[pkt].d[HBE_RSV] = 0;
cp->pak[pkt].d[HBE_BADL] = cp->pak[tpkt].d[RW_WBAL];	/* bad addr */
cp->pak[pkt].d[HBE_BADH] = cp->pak[tpkt].d[RW_WBAH];
rq_putr (cp, pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT);
return rq_putpkt (cp, pkt, TRUE);
}

/* Port last failure error log packet */

t_bool rq_plf (MSC *cp, uint32 err)
{
int32 pkt;

if (!rq_deqf (cp, &pkt)) return ERR;			/* get log pkt */
cp->pak[pkt].d[ELP_REFL] = 0;				/* ref = 0 */
cp->pak[pkt].d[ELP_REFH] = 0;
cp->pak[pkt].d[ELP_UN] = 0;				/* no unit */
cp->pak[pkt].d[ELP_SEQ] = 0;				/* no seq */
cp->pak[pkt].d[PLF_CIDA] = 0;				/* cntl ID */
cp->pak[pkt].d[PLF_CIDB] = 0;
cp->pak[pkt].d[PLF_CIDC] = 0;
cp->pak[pkt].d[PLF_CIDD] = (RQ_CLASS << PLF_CIDD_V_CLS) |
	(RQ_MODEL << PLF_CIDD_V_MOD);
cp->pak[pkt].d[PLF_VER] = (RQ_SVER << PLF_VER_V_SVER) |
	(RQ_HVER << PLF_VER_V_HVER);
cp->pak[pkt].d[PLF_ERR] = err;
rq_putr (cp, pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT);
cp->pak[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID);
return rq_putpkt (cp, pkt, TRUE);
}

/* Unit now available attention packet */

int32 rq_una (MSC *cp, int32 un)
{
int32 pkt;
uint32 lu = cp->ubase + un;
UNIT *uptr = rq_getucb (cp, lu);

if (uptr == NULL) return OK;				/* huh? */
if (!rq_deqf (cp, &pkt)) return ERR;			/* get log pkt */
cp->pak[pkt].d[RSP_REFL] = 0;				/* ref = 0 */
cp->pak[pkt].d[RSP_REFH] = 0;
cp->pak[pkt].d[RSP_UN] = lu;
cp->pak[pkt].d[RSP_RSV] = 0;
rq_putr_unit (cp, pkt, uptr, lu, FALSE);		/* fill unit fields */
rq_putr (cp, pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ);	/* fill std fields */
return rq_putpkt (cp, pkt, TRUE);
}

/* List handling

   rq_deqf	-	dequeue head of free list (fatal err if none)
   rq_deqh	-	dequeue head of list
   rq_enqh	-	enqueue at head of list
   rq_enqt	-	enqueue at tail of list
*/

t_bool rq_deqf (MSC *cp, int32 *pkt)
{
if (cp->freq == 0) return rq_fatal (cp, PE_NSR);	/* no free pkts?? */
cp->pbsy = cp->pbsy + 1;				/* cnt busy pkts */
*pkt = cp->freq;					/* head of list */
cp->freq = cp->pak[cp->freq].link;			/* next */
return OK;
}

int32 rq_deqh (MSC *cp, int32 *lh)
{
int32 ptr = *lh;					/* head of list */

if (ptr) *lh = cp->pak[ptr].link;			/* next */
return ptr;
}

void rq_enqh (MSC *cp, int32 *lh, int32 pkt)
{
if (pkt == 0) return;					/* any pkt? */
cp->pak[pkt].link = *lh;				/* link is old lh */
*lh = pkt;						/* pkt is new lh */
return;
}

void rq_enqt (MSC *cp, int32 *lh, int32 pkt)
{
if (pkt == 0) return;					/* any pkt? */
cp->pak[pkt].link = 0;					/* it will be tail */
if (*lh == 0) *lh = pkt;				/* if empty, enqh */
else {	uint32 ptr = *lh;				/* chase to end */
	while (cp->pak[ptr].link) ptr = cp->pak[ptr].link;
	cp->pak[ptr].link = pkt;  }			/* enq at tail */
return;
}

/* Packet and descriptor handling */

/* Get packet from command ring */

t_bool rq_getpkt (MSC *cp, int32 *pkt)
{
uint32 addr, desc;

if (!rq_getdesc (cp, &cp->cq, &desc)) return ERR;	/* get cmd desc */
if ((desc & UQ_DESC_OWN) == 0) {			/* none */
	*pkt = 0;					/* pkt = 0 */
	return OK;  }					/* no error */
if (!rq_deqf (cp, pkt)) return ERR;			/* get cmd pkt */
cp->hat = 0;						/* dsbl hst timer */
addr = desc & UQ_ADDR;					/* get Q22 addr */
if (Map_ReadW (addr + UQ_HDR_OFF, RQ_PKT_SIZE, cp->pak[*pkt].d, MAP))
	return rq_fatal (cp, PE_PRE);			/* read pkt */
return rq_putdesc (cp, &cp->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 rq_putpkt (MSC *cp, int32 pkt, t_bool qt)
{
uint32 addr, desc, lnt, cr;
DEVICE *dptr = rq_devmap[cp->cnum];

if (pkt == 0) return OK;				/* any packet? */
if (DEBUG_PRD (dptr)) fprintf (sim_deb,
	">>RQ%c: rsp=%04X, sts=%04X\n", 'A' + cp->cnum,
	cp->pak[pkt].d[RSP_OPF], cp->pak[pkt].d[RSP_STS]);
if (!rq_getdesc (cp, &cp->rq, &desc)) return ERR;	/* get rsp desc */
if ((desc & UQ_DESC_OWN) == 0) {			/* not valid? */
	if (qt) rq_enqt (cp, &cp->rspq, pkt);		/* normal? q tail */
	else rq_enqh (cp, &cp->rspq, pkt);		/* resp q call */
	sim_activate (dptr->units + RQ_QUEUE, rq_qtime);	/* activate q thrd */
	return OK;  }
addr = desc & UQ_ADDR;					/* get Q22 addr */
lnt = cp->pak[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 = (cp->credits >= 14)? 14: cp->credits;	/* max 14 credits */
	cp->credits = cp->credits - cr;			/* decr credits */
	cp->pak[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR);  }
if (Map_WriteW (addr + UQ_HDR_OFF, lnt, cp->pak[pkt].d, MAP))
	return rq_fatal (cp, PE_PWE);			/* write pkt */
rq_enqh (cp, &cp->freq, pkt);				/* pkt is free */
cp->pbsy = cp->pbsy - 1;				/* decr busy cnt */
if (cp->pbsy == 0) cp->hat = cp->htmo;			/* idle? strt hst tmr */
return rq_putdesc (cp, &cp->rq, desc);			/* release desc */
}

/* Get a descriptor from the host */

t_bool rq_getdesc (MSC *cp, struct uq_ring *ring, uint32 *desc)
{
uint32 addr = ring->ba + ring->idx;
uint16 d[2];

if (Map_ReadW (addr, 4, d, MAP))			/* fetch desc */
	return rq_fatal (cp, 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 rq_putdesc (MSC *cp, 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, MAP))			/* store desc */
	return rq_fatal (cp, PE_QWE);			/* err? dead */
if (desc & UQ_DESC_F) {					/* was F set? */
	if (ring->lnt <= 4) rq_ring_int (cp, ring);	/* lnt = 1? intr */
	else {						/* prv desc */
	    prva = ring->ba + ((ring->idx - 4) & (ring->lnt - 1));
	    if (Map_ReadW (prva, 4, d, MAP))		/* read prv */
		return rq_fatal (cp, PE_QRE);
	    prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16);
	    if (prvd & UQ_DESC_OWN) rq_ring_int (cp, ring);  }  }
ring->idx = (ring->idx + 4) & (ring->lnt - 1);
return OK;
}

/* Get unit descriptor for logical unit */

UNIT *rq_getucb (MSC *cp, uint32 lu)
{
DEVICE *dptr = rq_devmap[cp->cnum];
UNIT *uptr;

if ((lu < cp->ubase) || (lu >= (cp->ubase + RQ_NUMDR)))
	return NULL;
uptr = dptr->units + (lu % RQ_NUMDR);
if (uptr->flags & UNIT_DIS) return NULL;
return uptr;
}

/* Hack unit flags */

void rq_setf_unit (MSC *cp, int32 pkt, UNIT *uptr)
{
uptr->uf = cp->pak[pkt].d[ONL_UFL] & UF_MSK;		/* settable flags */
if ((cp->pak[pkt].d[CMD_MOD] & MD_SWP) &&		/* swre wrp enb? */
	(cp->pak[pkt].d[ONL_UFL] & UF_WPS))		/* swre wrp on? */
	uptr->uf = uptr->uf | UF_WPS;			/* simon says... */
return;
}

/* Unit response fields */

void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all)
{
uint32 dtyp = GET_DTYPE (uptr->flags);			/* get drive type */
uint32 maxlbn = uptr->capac / RQ_NUMBY;			/* get max lbn */

cp->pak[pkt].d[ONL_MLUN] = lu;				/* unit */
cp->pak[pkt].d[ONL_UFL] = uptr->uf | UF_RPL | RQ_WPH (uptr) | RQ_RMV (uptr);
cp->pak[pkt].d[ONL_RSVL] = 0;				/* reserved */
cp->pak[pkt].d[ONL_RSVH] = 0;
cp->pak[pkt].d[ONL_UIDA] = lu;				/* UID low */
cp->pak[pkt].d[ONL_UIDB] = 0;
cp->pak[pkt].d[ONL_UIDC] = 0;
cp->pak[pkt].d[ONL_UIDD] = (UID_DISK << ONL_UIDD_V_CLS) |
	(drv_tab[dtyp].mod << ONL_UIDD_V_MOD);		/* UID hi */
PUTP32 (pkt, ONL_MEDL, drv_tab[dtyp].med);		/* media type */
if (all) {						/* if long form */
	PUTP32 (pkt, ONL_SIZL, maxlbn);			/* user LBNs */
	cp->pak[pkt].d[ONL_VSNL] = 01234 + lu;		/* vol serial # */
	cp->pak[pkt].d[ONL_VSNH] = 0;  }
return;
}

/* UQ_HDR and RSP_OP fields */

void rq_putr (MSC *cp, int32 pkt, uint32 cmd, uint32 flg,
	      uint32 sts, uint32 lnt, uint32 typ)
{
cp->pak[pkt].d[RSP_OPF] = (cmd << RSP_OPF_V_OPC) |	/* set cmd, flg */
	(flg << RSP_OPF_V_FLG);
cp->pak[pkt].d[RSP_STS] = sts;
cp->pak[pkt].d[UQ_HLNT] = lnt;				/* length */
cp->pak[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) |	/* type, cid */
	(UQ_CID_MSCP << UQ_HCTC_V_CID);			/* clr credits */
return;
}

/* Post interrupt during init */

void rq_init_int (MSC *cp)
{
if ((cp->s1dat & SA_S1H_IE) &&				/* int enab & */
    (cp->s1dat & SA_S1H_VEC)) rq_setint (cp);		/* ved set? int */
return;
}

/* Post interrupt during putpkt - note that NXMs are ignored! */

void rq_ring_int (MSC *cp, struct uq_ring *ring)
{
uint32 iadr = cp->comm + ring->ioff;			/* addr intr wd */
uint16 flag = 1;

Map_WriteW (iadr, 2, &flag, MAP);			/* write flag */
if (cp->s1dat & SA_S1H_VEC) rq_setint (cp); 		/* if enb, intr */
return;
}

/* Set RQ interrupt */

void rq_setint (MSC *cp)
{
cp->irq = 1;						/* set ctrl int */
SET_INT (RQ);						/* set master int */
return;
}

/* Clear RQ interrupt */

void rq_clrint (MSC *cp)
{
int32 i;
MSC *ncp;

cp->irq = 0;						/* clr ctrl int */
for (i = 0; i < RQ_NUMCT; i++) {			/* loop thru ctrls */
	ncp = rq_ctxmap[i];				/* get context */
	if (ncp->irq) {					/* other interrupt? */
	    SET_INT (RQ);				/* yes, set master */
	    return;  }  }
CLR_INT (RQ);						/* no, clr master */
return;
}

/* Return interrupt vector */

int32 rq_inta (void)
{
int32 i;
MSC *ncp;
DEVICE *dptr;
DIB *dibp;

for (i = 0; i < RQ_NUMCT; i++) {			/* loop thru ctrl */
	ncp = rq_ctxmap[i];				/* get context */
	if (ncp->irq) {					/* ctrl int set? */
	    dptr = rq_devmap[i];			/* get device */
	    dibp = (DIB *) dptr->ctxt;			/* get DIB */
	    rq_clrint (ncp);				/* clear int req */
	    return dibp->vec;  }  }			/* return vector */
return 0;						/* no intr req */
}

/* Fatal error */

t_bool rq_fatal (MSC *cp, uint32 err)
{
DEVICE *dptr = rq_devmap[cp->cnum];

if (DEBUG_PRD (dptr))
	fprintf (sim_deb, ">>RQ%c: fatal err=%X\n", 'A' + cp->cnum, err);
rq_reset (rq_devmap[cp->cnum]);				/* reset device */
cp->sa = SA_ER | err;					/* SA = dead code */
cp->csta = CST_DEAD;					/* state = dead */
cp->perr = err;						/* save error */
return ERR;
}

/* Set/clear hardware write lock */

t_stat rq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc)
{
uint32 dtyp = GET_DTYPE (uptr->flags);			/* get drive type */

if (drv_tab[dtyp].flgs & RQDF_RO) return SCPE_NOFNC;	/* not on read only */
return SCPE_OK;
}

/* Show write lock status */

t_stat rq_show_wlk (FILE *st, UNIT *uptr, int32 val, void *desc)
{
uint32 dtyp = GET_DTYPE (uptr->flags);			/* get drive type */

if (drv_tab[dtyp].flgs & RQDF_RO) fprintf (st, "read only");
else if (uptr->flags & UNIT_WPRT) fprintf (st, "write locked");
else fprintf (st, "write enabled");
return SCPE_OK;
}

/* Set unit type (and capacity if user defined) */

t_stat rq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc)
{
uint32 cap;
uint32 max = sim_taddr_64? RA8U_EMAXC: RA8U_MAXC;
t_stat r;

if ((val < 0) || (val > RA8U_DTYPE) || ((val != RA8U_DTYPE) && cptr))
	return SCPE_ARG;
if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
if (cptr) {
	cap = (int32) get_uint (cptr, 10, max, &r);
	if ((r != SCPE_OK) || (cap < RA8U_MINC)) return SCPE_ARG;
	drv_tab[val].lbn = cap * 1954;  }
uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (val << UNIT_V_DTYPE);
uptr->capac = ((t_addr) drv_tab[val].lbn) * RQ_NUMBY;
return SCPE_OK;
}

/* Show unit type */

t_stat rq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc)
{
fprintf (st, "%s", drv_tab[GET_DTYPE (uptr->flags)].name);
return SCPE_OK;
}

/* Device attach */

t_stat rq_attach (UNIT *uptr, char *cptr)
{
int32 dtyp = GET_DTYPE (uptr->flags);
MSC *cp = rq_ctxmap[uptr->cnum];
t_stat r;

r = attach_unit (uptr, cptr);
if (r != SCPE_OK) return r;
if (cp->csta == CST_UP) uptr->flags = uptr->flags | UNIT_ATP;
return SCPE_OK;
}

/* Device detach */

t_stat rq_detach (UNIT *uptr)
{
t_stat r;

r = detach_unit (uptr);					/* detach unit */
if (r != SCPE_OK) return r;
uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP);	/* clr onl, atn pend */
uptr->uf = 0;						/* clr unit flgs */
return SCPE_OK;
} 

/* Device reset */

t_stat rq_reset (DEVICE *dptr)
{
int32 i, j, cidx;
UNIT *uptr;
MSC *cp;
DIB *dibp = (DIB *) dptr->ctxt;

for (i = 0, cidx = -1; i < RQ_NUMCT; i++) {		/* find ctrl num */
	if (rq_devmap[i] == dptr) cidx = i;  }
if (cidx < 0) return SCPE_IERR;				/* not found??? */
cp = rq_ctxmap[cidx];					/* get context */
cp->cnum = cidx;					/* init index */
cp->ubase = cidx * RQ_NUMDR;				/* init unit base */
cp->csta = CST_S1;					/* init stage 1 */
cp->s1dat = 0;						/* no S1 data */
dibp->vec = 0;						/* no vector */
cp->comm = 0;						/* no comm region */
cp->sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP;	/* init SA val */
cp->cflgs = CF_RPL;					/* ctrl flgs off */
cp->htmo = RQ_DHTMO;					/* default timeout */
cp->hat = cp->htmo;					/* default timer */
cp->cq.ba = cp->cq.lnt = cp->cq.idx = 0;		/* clr cmd ring */
cp->rq.ba = cp->rq.lnt = cp->rq.idx = 0;		/* clr rsp ring */
cp->credits = (RQ_NPKTS / 2) - 1;			/* init credits */
cp->freq = 1;						/* init free list */
for (i = 0; i < RQ_NPKTS; i++) {			/* all pkts free */
	if (i) cp->pak[i].link = (i + 1) & RQ_M_NPKTS;
	else cp->pak[i].link = 0;
	for (j = 0; j < RQ_PKT_SIZE_W; j++) cp->pak[i].d[j] = 0;  }
cp->rspq = 0;						/* no q'd rsp pkts */
cp->pbsy = 0;						/* all pkts free */
cp->pip = 0;						/* not polling */
rq_clrint (cp);						/* clr intr req */
for (i = 0; i < (RQ_NUMDR + 2); i++) {			/* init units */
	uptr = dptr->units + i;
	sim_cancel (uptr);				/* clr activity */
	uptr->cnum = cidx;				/* set ctrl index */
	uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP);
	uptr->uf = 0;					/* clr unit flags */
	uptr->cpkt = uptr->pktq = 0;  }			/* clr pkt q's */
if (rqxb == NULL) rqxb = calloc (RQ_MAXFR >> 1, sizeof (unsigned int16));
if (rqxb == NULL) return SCPE_MEM;
return auto_config (0, 0);				/* run autoconfig */
}

/* 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))

static const uint16 boot_rom[] = {

	0042125,			/* st: "UD" */

	/* Four step init process */

	0012706, 0016000,		/*   mov  #st,sp */
	0012700, 0000000,		/*   mov  #unit,r0 */
	0012701, 0172150,		/*   mov  #172150, r1	; ip addr */
	0012704, 0016162,		/*   mov  #it, r4 */
	0012705, 0004000,		/*   mov  #4000,r5	; s1 mask */
	0010102,			/*   mov  r1,r2 */
	0005022,			/*   clr  (r2)+		; init */
	0005712,			/* 10$: tst (r2)	; err? */
	0100001,			/*   bpl  20$ */
	0000000,			/*   halt */
	0030512,			/* 20$: bit r5,(r2)	; step set? */
	0001773,			/*   beq  10$		; wait */
	0012412,			/*   mov  (r4)+,(r2)	; send next */
	0006305,			/*   asl  r5		; next mask */
	0100370,			/*   bpl 10$		; s4 done? */

	/* Send ONL, READ commands */

	0105714,			/* 30$:	tstb	(r4)	; end tbl? */
	0001434,			/*   beq  done		; 0 = yes */
	0012702, 0007000,		/*   mov  #rpkt-4,r2	; clr pkts */
	0005022,			/* 40$: clr (r2)+ */
	0020227, 0007204,		/*   cmp  r2,#comm */
	0103774,			/*   blo  40$ */
	0112437, 0007100,		/*   movb (r4)+,cpkt-4	; set lnt */
	0110037, 0007110,		/*   movb r0,cpkt+4	; set unit */
	0112437, 0007114,		/*   movb (r4)+,cpkt+10	; set op */
	0112437, 0007121,		/*   movb (r4)+,cpkt+15	; set param */
	0012722, 0007004,		/*   mov  #rpkt,(r2)+	; rq desc */
	0010522,			/*   mov  r5,(r2)+	; rq own */
	0012722, 0007104,		/*   mov  #ckpt,(r2)+	; cq desc */
	0010512,			/*   mov  r5,(r2)	; cq own */
	0024242,			/*   cmp  -(r2),-(r2)	; back up */
	0005711,			/*   tst  (r1)		; wake ctrl */
	0005712,			/* 50$: tst (r2)	; rq own clr? */
	0100776,			/*   bmi  50$		; wait */
	0005737, 0007016,		/*   tst  rpkt+12	; stat ok? */
	0001743,			/*   beq  30$		; next cmd */
	0000000,			/*   halt */

	/* Boot block read in, jump to 0 */

	0005011,			/* done: clr (r1)	; for M+ */
	0005003,			/*   clr  r3 */
	0012704, BOOT_START+020,	/*   mov  #st+020,r4 */
	0005005,			/*   clr  r5 */
	0005007,			/*   clr  pc */

	/* Data */

	0100000,			/* it: no ints, ring sz = 1 */
	0007204,			/*    .word comm */
	0000000,			/*    .word 0 */
	0000001,			/*    .word 1 */
	0004420,			/*   .byte 20,11 */
	0020000,			/*   .byte 0,40 */
	0001041,			/*   .byte 41,2 */
	0000000
};

t_stat rq_boot (int32 unitno, DEVICE *dptr)
{
int32 i;
extern int32 saved_PC;
extern uint16 *M;
DIB *dibp = (DIB *) dptr->ctxt;

for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
M[BOOT_UNIT >> 1] = unitno & 3;
M[BOOT_CSR >> 1] = dibp->ba & DMASK;
saved_PC = BOOT_ENTRY;
return SCPE_OK;
}

#else

t_stat rq_boot (int32 unitno, DEVICE *dptr)
{
return SCPE_NOFNC;
}
#endif

/* Special show commands */

void rq_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, MAP)) {
		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 rq_show_pkt (FILE *st, MSC *cp, 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 < RQ_SH_MAX; i = i + RQ_SH_PPL) {
	fprintf (st, " %2d:", i);
	for (j = i; j < (i + RQ_SH_PPL); j++)
#if defined (VM_PDP11)
		fprintf (st, " %06o", cp->pak[pkt].d[j]);
#else
		fprintf (st, " %04x", cp->pak[pkt].d[j]);
#endif
		fprintf (st, "\n");
	}
return;
}

t_stat rq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc)
{
MSC *cp = rq_ctxmap[uptr->cnum];
DEVICE *dptr = rq_devmap[uptr->cnum];
int32 pkt, u;

u = uptr - dptr->units;
if (cp->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);
	rq_show_pkt (st, cp, uptr->cpkt);
	if (pkt = uptr->pktq) {
		do {	fprintf (st, "Unit %d queued ", u);
			rq_show_pkt (st, cp, pkt);  }
		while (pkt = cp->pak[pkt].link);  }  }
else fprintf (st, "Unit %d queues are empty\n", u);
return SCPE_OK;
}

t_stat rq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc)
{
MSC *cp = rq_ctxmap[uptr->cnum];
DEVICE *dptr = rq_devmap[uptr->cnum];
int32 i, pkt;

if (cp->csta != CST_UP) {
    fprintf (st, "Controller is not initialized\n");
    return SCPE_OK;  }
if (val & RQ_SH_RI) {
    if (cp->pip) fprintf (st, "Polling in progress, host timer = %d\n", cp->hat);
    else fprintf (st, "Host timer = %d\n", cp->hat);
    fprintf (st, "Command ");
    rq_show_ring (st, &cp->cq);
    fprintf (st, "Response ");
    rq_show_ring (st, &cp->rq);
    }
if (val & RQ_SH_FR) {
    if (pkt = cp->freq) {
	for (i = 0; pkt != 0; i++, pkt = cp->pak[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 & RQ_SH_RS) {
    if (pkt = cp->rspq) {
	do {	fprintf (st, "Response ");
		rq_show_pkt (st, cp, pkt);  }
	while (pkt = cp->pak[pkt].link);  }
    else fprintf (st, "Response queue is empty\n");
    }
if (val & RQ_SH_UN) {
    for (i = 0; i < RQ_NUMDR; i++)
	rq_show_unitq (st, dptr->units + i, 0, desc);
    }
return SCPE_OK;
}
