RESTRICTION: The FP15 and XVM features of the PDP-15 are only partially debugged. Do NOT enable these features for normal operations. 1. New Features in 3.0-1 1.1 PDP-1 - Added block loader format support to LOAD. - Changed BOOT PTR to allow loading of all of the first bank of memory. 1.2 PDP-18b Family - Added PDP-4 EAE support. - Added PDP-15 FP15 support. - Added PDP-15 XVM support. - Added PDP-15 "re-entrancy ECO". - Added PDP-7, PDP-9, PDP-15 hardware RIM loader support in BOOT PTR. 2. Bugs Fixed in 3.0-1 2.1 PDP-11/VAX - Fixed bug in user disk size (found by Chaskiel M Grundman). 2.2 PDP-1 - Updated CPU, line printer, standard devices to detect indefinite I/O wait. - Fixed incorrect logical, missing activate, break in drum simulator. - Fixed bugs in instruction decoding, overprinting for line printer. 2.3 PDP-10 - Fixed bug in RP read header. 2.4 PDP-18b Family - Fixed bug in PDP-4 line printer overprinting. - Fixed bug in PDP-15 memory protect/skip interaction. - Fixed bug in RF set size routine. - Increased PTP TIME for PDP-15 operating systems. 2.5 PDP-8 - Fixed bug in DF, RF set size routine. 2.6 Nova - Fixed bug in DSK set size routine. 2.7 1401 - Revised fetch to model hardware more closely. 2.8 Ibm1130 - Fixed bugs found by APL 1130. 2.9 Altairz80 - Fixed bug in real-time clock on Windows host. 2.10 HP2100 -- Fixed DR drum sizes. -- Fixed DR variable capacity interaction with SAVE/RESTORE. 3. New Features in 3.0 vs prior releases 3.1 SCP and Libraries - Added ASSIGN/DEASSIGN (logical name) commands. - Changed RESTORE to unconditionally detach files. - Added E11 and TPC format support to magtape library. - Fixed bug in SHOW CONNECTIONS. - Added USE_ADDR64 support 3.2 All magtapes - Magtapes support SIMH format, E11 format, and TPC format (read only). - SET <tape_unit> FORMAT=format sets the specified tape unit's format. - SHOW <tape_unit> FORMAT displays the specified tape unit's format. - Tape format can also be set as part of the ATTACH command, using the -F switch. 3.3 VAX - VAX can be compiled without USE_INT64. - If compiled with USE_INT64 and USE_ADDR64, RQ and TQ controllers support files > 2GB. - VAX ROM has speed control (SET ROM DELAY/NODELAY). 4. Bugs Fixed in 3.0 vs prior releases 4.1 VAX - Fixed CVTfi bug: integer overflow not set if exponent out of range - Fixed EMODx bugs: o First and second operands reversed o Separated fraction received wrong exponent o Overflow calculation on separated integer incorrect o Fraction not set to zero if exponent out of range - Fixed interval timer and ROM access to pass power-up self-test even on very fast host processors (fixes from Mark Pizzolato). 4.2 1401 - Fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS. - Fixed MCE bug, BS off by 1 if zero suppress. - Fixed chaining bug, D lost if return to SCP. - Fixed H branch, branch occurs after continue. - Added check for invalid 8 character MCW, LCA. - Fixed magtape load-mode end of record response. 4.3 Nova - Fixed DSK variable size interaction with restore. 4.4 PDP-1 - Fixed DT variable size interaction with restore. 4.5 PDP-11 - Fixed DT variable size interaction with restore. - Fixed bug in MMR1 update (found by Tim Stark). - Added XQ features and fixed bugs: o Corrected XQ interrupts on IE state transition (code by Tom Evans). o Added XQ interrupt clear on soft reset. o Removed XQ interrupt when setting XL or RL (multiple people). o Added SET/SHOW XQ STATS. o Added SHOW XQ FILTERS. o Added ability to split received packet into multiple buffers. o Added explicit runt and giant packet processing. 4.6 PDP-18B - Fixed DT, RF variable size interaction with restore. - Fixed MT bug in MTTR. 4.7 PDP-8 - Fixed DT, DF, RF, RX variable size interaction with restore. - Fixed MT bug in SKTR. 4.8 HP2100 - Fixed bug in DP (13210A controller only), DQ read status. - Fixed bug in DP, DQ seek complete. 4.9 GRI - Fixed bug in SC queue pointer management.
2316 lines
80 KiB
C
2316 lines
80 KiB
C
/* pdp11_rq.c: MSCP disk controller simulator
|
||
|
||
Copyright (c) 2002-2003, 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
|
||
|
||
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 int32 cpu_log;
|
||
extern UNIT cpu_unit;
|
||
extern FILE *sim_log;
|
||
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 };
|
||
|
||
/* 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 };
|
||
|
||
/* 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 };
|
||
|
||
/* 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 };
|
||
|
||
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 (DBG_LOG (LOG_RQ)) fprintf (sim_log,
|
||
">>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 (DBG_LOG (LOG_RQ)) fprintf (sim_log,
|
||
">>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 (DBG_LOG (LOG_RQ)) {
|
||
fprintf (sim_log, ">>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_log, "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 = 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, err, tbc, abc, wwc;
|
||
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 = fseek_ext (uptr->fileref, da, SEEK_SET); /* set pos */
|
||
if (!err) fxwrite (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 = fseek_ext (uptr->fileref, da, SEEK_SET);
|
||
if (!err) fxwrite (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 = fseek_ext (uptr->fileref, da, SEEK_SET); /* set pos */
|
||
if (!err) {
|
||
i = fxread (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 (DBG_LOG (LOG_RQ)) fprintf (sim_log,
|
||
">>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 */
|
||
|
||
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, drv_tab[dtyp].lbn); /* 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 | SA_S1H_VEC)) rq_setint (cp);
|
||
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)
|
||
{
|
||
if (DBG_LOG (LOG_RQ))
|
||
fprintf (sim_log, ">>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 << (20 - 9); }
|
||
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 (and capacity if user defined) */
|
||
|
||
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;
|
||
|
||
uptr->capac = drv_tab[dtyp].lbn * RQ_NUMBY;
|
||
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;
|
||
}
|