WARNING: V2.10 has reorganized and renamed some of the definition files for the PDP-10, PDP-11, and VAX. Be sure to delete all previous source files before you unpack the Zip archive, or unpack it into a new directory structure. WARNING: V2.10 has a new, more comprehensive save file format. Restoring save files from previous releases will cause 'invalid register' errors and loss of CPU option flags, device enable/ disable flags, unit online/offline flags, and unit writelock flags. WARNING: If you are using Visual Studio .NET through the IDE, be sure to turn off the /Wp64 flag in the project settings, or dozens of spurious errors will be generated. WARNING: Compiling Ethernet support under Windows requires extra steps; see the Ethernet readme file. Ethernet support is currently available only for Windows, Linux, NetBSD, and OpenBSD. 1. New Features 1.1 SCP and Libraries - The VT emulation package has been replaced by the capability to remote the console to a Telnet session. Telnet clients typically have more complete and robust VT100 emulation. - Simulated devices may now have statically allocated buffers, in addition to dynamically allocated buffers or disk-based data stores. - The DO command now takes substitutable arguments (max 9). In command files, %n represents substitutable argument n. - The initial command line is now interpreted as the command name and substitutable arguments for a DO command. This is backward compatible to prior versions. - The initial command line parses switches. -Q is interpreted as quiet mode; informational messages are suppressed. - The HELP command now takes an optional argument. HELP <cmd> types help on the specified command. - Hooks have been added for implementing GUI-based consoles, as well as simulator-specific command extensions. A few internal data structures and definitions have changed. - Two new routines (tmxr_open_master, tmxr_close_master) have been added to sim_tmxr.c. The calling sequence for sim_accept_conn has been changed in sim_sock.c. - The calling sequence for the VM boot routine has been modified to add an additional parameter. - SAVE now saves, and GET now restores, controller and unit flags. - Library sim_ether.c has been added for Ethernet support. 1.2 VAX - Non-volatile RAM (NVR) can behave either like a memory or like a disk-based peripheral. If unattached, it behaves like memory and is saved and restored by SAVE and RESTORE, respectively. If attached, its contents are loaded from disk by ATTACH and written back to disk at DETACH and EXIT. - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape) has been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from vax_stddev.c and now references a common implementation file, dec_pt.h. - Examine and deposit switches now work on all devices, not just the CPU. - Device address conflicts are not detected until simulation starts. 1.3 PDP-11 - SHOW <device> VECTOR displays the device's interrupt vector. Most devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape), RK611/RK06/RK07 (cartridge disk), RX211 (double density floppy), and KW11P programmable clock have been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from pdp11_stddev.c and now references a common implementation file, dec_pt.h. - Device bootstraps now use the actual CSR specified by the SET ADDRESS command, rather than just the default CSR. Note that PDP-11 operating systems may NOT support booting with non-standard addresses. - Specifying more than 256KB of memory, or changing the bus configuration, causes all peripherals that are not compatible with the current bus configuration to be disabled. - Device address conflicts are not detected until simulation starts. 1.4 PDP-10 - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The RX211 (double density floppy) has been added; it is off by default. - The paper tape now references a common implementation file, dec_pt.h. - Device address conflicts are not detected until simulation starts. 1.5 PDP-1 - DECtape (then known as MicroTape) support has been added. - The line printer and DECtape can be disabled and enabled. 1.6 PDP-8 - The RX28 (double density floppy) has been added as an option to the existing RX8E controller. - SHOW <device> DEVNO displays the device's device number. Most devices allow the device number to be changed with SET <device> DEVNO=nnn. - Device number conflicts are not detected until simulation starts. 1.7 IBM 1620 - The IBM 1620 simulator has been released. 1.8 AltairZ80 - A hard drive has been added for increased storage. - Several bugs have been fixed. 1.9 HP 2100 - The 12845A has been added and made the default line printer (LPT). The 12653A has been renamed LPS and is off by default. It also supports the diagnostic functions needed to run the DCPC and DMS diagnostics. - The 12557A/13210A disk defaults to the 13210A (7900/7901). - The 12559A magtape is off by default. - New CPU options (EAU/NOEAU) enable/disable the extended arithmetic instructions for the 2116. These instructions are standard on the 2100 and 21MX. - New CPU options (MPR/NOMPR) enable/disable memory protect for the 2100 and 21MX. - New CPU options (DMS/NODMS) enable/disable the dynamic mapping instructions for the 21MX. - The 12539 timebase generator autocalibrates. 1.10 Simulated Magtapes - Simulated magtapes recognize end of file and the marker 0xFFFFFFFF as end of medium. Only the TMSCP tape simulator can generate an end of medium marker. - The error handling in simulated magtapes was overhauled to be consistent through all simulators. 1.11 Simulated DECtapes - Added support for RT11 image file format (256 x 16b) to DECtapes. 2. Release Notes 2.1 Bugs Fixed - TS11/TSV05 was not simulating the XS0_MOT bit, causing failures under VMS. In addition, two of the CTL options were coded interchanged. - IBM 1401 tape was not setting a word mark under group mark for load mode reads. This caused the diagnostics to crash. - SCP bugs in ssh_break and set_logon were fixed (found by Dave Hittner). - Numerous bugs in the HP 2100 extended arithmetic, floating point, 21MX, DMS, and IOP instructions were fixed. Bugs were also fixed in the memory protect and DMS functions. The moving head disks (DP, DQ) were revised to simulate the hardware more accurately. Missing functions in DQ (address skip, read address) were added. 2.2 HP 2100 Debugging - The HP 2100 CPU nows runs all of the CPU diagnostics. - The peripherals run most of the peripheral diagnostics. There is still a problem in overlapped seek operation on the disks. See the file hp2100_diag.txt for details. 3. In Progress These simulators are not finished and are available in a separate Zip archive distribution. - Interdata 16b/32b: coded, partially tested. See the file id_diag.txt for details. - SDS 940: coded, partially tested.
1984 lines
68 KiB
C
1984 lines
68 KiB
C
/* pdp11_tq.c: TQK50 tape controller simulator
|
||
|
||
Copyright (c) 2002, Robert M Supnik
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a
|
||
copy of this software and associated documentation files (the "Software"),
|
||
to deal in the Software without restriction, including without limitation
|
||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
and/or sell copies of the Software, and to permit persons to whom the
|
||
Software is furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in
|
||
all copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
||
Except as contained in this notice, the name of Robert M Supnik shall not
|
||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||
in this Software without prior written authorization from Robert M Supnik.
|
||
|
||
tq TQK50 tape controller
|
||
|
||
17-Oct-02 RMS Fixed bug in read reverse (found by Hans Pufal)
|
||
*/
|
||
|
||
#if defined (USE_INT64)
|
||
#include "vax_defs.h"
|
||
#define VM_VAX 1
|
||
#define TQ_RDX 16
|
||
|
||
#else
|
||
#include "pdp11_defs.h"
|
||
#define VM_PDP11 1
|
||
#define TQ_RDX 8
|
||
extern int32 cpu_18b, cpu_ubm;
|
||
#endif
|
||
|
||
#include "pdp11_uqssp.h"
|
||
#include "pdp11_mscp.h"
|
||
|
||
#define UF_MSK (UF_SCH|UF_VSS|UF_CMR|UF_CMW) /* settable flags */
|
||
|
||
#define TQ_SH_MAX 24 /* max display wds */
|
||
#define TQ_SH_PPL 8 /* wds per line */
|
||
#define TQ_SH_DPL 4 /* desc per line */
|
||
#define TQ_SH_RI 001 /* show rings */
|
||
#define TQ_SH_FR 002 /* show free q */
|
||
#define TQ_SH_RS 004 /* show resp q */
|
||
#define TQ_SH_UN 010 /* show unit q's */
|
||
|
||
#define TQ_CLASS 1 /* TQK50 class */
|
||
#define TQ_DHTMO 0 /* def host timeout */
|
||
#define TQ_DCTMO 120 /* def ctrl timeout */
|
||
#define TQ_NUMDR 4 /* # drives */
|
||
#define TQ_MAXFR (1 << 16) /* max xfer */
|
||
|
||
#define UNIT_V_ONL (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_SXC (UNIT_V_UF + 3) /* serious exc */
|
||
#define UNIT_V_POL (UNIT_V_UF + 4) /* position lost */
|
||
#define UNIT_V_TMK (UNIT_V_UF + 5) /* tape mark seen */
|
||
#define UNIT_ONL (1 << UNIT_V_ONL)
|
||
#define UNIT_WLK (1 << UNIT_V_WLK)
|
||
#define UNIT_ATP (1 << UNIT_V_ATP)
|
||
#define UNIT_SXC (1 << UNIT_V_SXC)
|
||
#define UNIT_POL (1 << UNIT_V_POL)
|
||
#define UNIT_TMK (1 << UNIT_V_TMK)
|
||
#define cpkt u3 /* current packet */
|
||
#define pktq u4 /* packet queue */
|
||
#define uf buf /* settable unit flags */
|
||
#define objp wait /* object position */
|
||
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */
|
||
#define TQ_WPH(u) ((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 tq_comm tq_rq.ba
|
||
|
||
#define ERR 0 /* must be SCPE_OK! */
|
||
#define OK 1
|
||
|
||
#define CMF_IMM 0x10000 /* immediate */
|
||
#define CMF_SEQ 0x20000 /* sequential */
|
||
#define CMF_WR 0x40000 /* write */
|
||
#define CMF_RW 0x80000 /* resp to GCS */
|
||
|
||
/* Internal packet management */
|
||
|
||
#define TQ_NPKTS 32 /* # packets (pwr of 2) */
|
||
#define TQ_M_NPKTS (TQ_NPKTS - 1) /* mask */
|
||
#define TQ_PKT_SIZE_W 32 /* payload size (wds) */
|
||
#define TQ_PKT_SIZE (TQ_PKT_SIZE_W * sizeof (int16))
|
||
|
||
struct tqpkt {
|
||
int16 link; /* link to next */
|
||
uint16 d[TQ_PKT_SIZE_W]; }; /* data */
|
||
|
||
/* Packet payload extraction and insertion */
|
||
|
||
#define GETP(p,w,f) ((tq_pkt[p].d[w] >> w##_V_##f) & w##_M_##f)
|
||
#define GETP32(p,w) (((uint32) tq_pkt[p].d[w]) | \
|
||
(((uint32) tq_pkt[p].d[(w)+1]) << 16))
|
||
#define PUTP32(p,w,x) tq_pkt[p].d[w] = (x) & 0xFFFF; \
|
||
tq_pkt[p].d[(w)+1] = ((x) >> 16) & 0xFFFF
|
||
|
||
/* Controller and device types - TQK50 must be swre rev 5 or later */
|
||
|
||
#define TQ5_TYPE 0 /* TK50 */
|
||
#define TQ5_UQPM 3 /* UQ port ID */
|
||
#define TQ5_CMOD 9 /* ctrl ID */
|
||
#define TQ5_UMOD 3 /* unit ID */
|
||
#define TQ5_MED 0x6D68B032 /* media ID */
|
||
#define TQ5_CREV ((1 << 8) | 5) /* ctrl revs */
|
||
#define TQ5_FREV 0 /* formatter revs */
|
||
#define TQ5_UREV 0 /* unit revs */
|
||
#define TQ5_CAP (94 * (1 << 20)) /* capacity */
|
||
#define TQ5_FMT (TF_CTP|TF_CTP_LO) /* menu */
|
||
|
||
#define TQ7_TYPE 1 /* TK70 */
|
||
#define TQ7_UQPM 14 /* UQ port ID */
|
||
#define TQ7_CMOD 14 /* ctrl ID */
|
||
#define TQ7_UMOD 11 /* unit ID */
|
||
#define TQ7_MED 0x6A68B046 /* media ID */
|
||
#define TQ7_CREV ((1 << 8) | 5) /* ctrl revs */
|
||
#define TQ7_FREV 0 /* formatter revs */
|
||
#define TQ7_UREV 0 /* unit revs */
|
||
#define TQ7_CAP (300 * (1 << 20)) /* capacity */
|
||
#define TQ7_FMT (TF_CTP|TF_CTP_LO) /* menu */
|
||
|
||
#define TQ8_TYPE 2 /* TU81 */
|
||
#define TQ8_UQPM 5 /* UQ port ID */
|
||
#define TQ8_CMOD 5 /* ctrl ID */
|
||
#define TQ8_UMOD 2 /* unit ID */
|
||
#define TQ8_MED 0x6D695051 /* media ID */
|
||
#define TQ8_CREV ((1 << 8) | 5) /* ctrl revs */
|
||
#define TQ8_FREV 0 /* formatter revs */
|
||
#define TQ8_UREV 0 /* unit revs */
|
||
#define TQ8_CAP (180 * (1 << 20)) /* capacity */
|
||
#define TQ8_FMT (TF_9TK|TF_9TK_GRP) /* menu */
|
||
|
||
#define TQ_DRV(d) \
|
||
d##_UQPM, \
|
||
d##_CMOD, d##_MED, d##_FMT, d##_CAP, \
|
||
d##_UMOD, d##_CREV, d##_FREV, d##_UREV
|
||
|
||
#define TEST_EOT(u) (uptr->pos > drv_tab[tq_typ].cap)
|
||
|
||
struct drvtyp {
|
||
uint32 uqpm; /* UQ port model */
|
||
uint32 cmod; /* ctrl model */
|
||
uint32 med; /* MSCP media */
|
||
uint32 fmt; /* flags */
|
||
uint32 cap; /* capacity */
|
||
uint32 umod; /* unit model */
|
||
uint32 cver;
|
||
uint32 fver;
|
||
uint32 uver;
|
||
};
|
||
|
||
static struct drvtyp drv_tab[] = {
|
||
{ TQ_DRV (TQ5) },
|
||
{ TQ_DRV (TQ7) },
|
||
{ TQ_DRV (TQ8) }
|
||
};
|
||
|
||
/* Data */
|
||
|
||
extern int32 int_req[IPL_HLVL];
|
||
extern int32 tmr_poll, clk_tps;
|
||
extern int32 cpu_log;
|
||
extern UNIT cpu_unit;
|
||
extern FILE *sim_log;
|
||
|
||
uint8 *tqxb = NULL; /* xfer buffer */
|
||
uint32 tq_sa = 0; /* status, addr */
|
||
uint32 tq_saw = 0; /* written data */
|
||
uint32 tq_s1dat = 0; /* S1 data */
|
||
uint32 tq_csta = 0; /* ctrl state */
|
||
uint32 tq_perr = 0; /* last error */
|
||
uint32 tq_cflgs = 0; /* ctrl flags */
|
||
uint32 tq_prgi = 0; /* purge int */
|
||
uint32 tq_pip = 0; /* poll in progress */
|
||
struct uq_ring tq_cq = { 0 }; /* cmd ring */
|
||
struct uq_ring tq_rq = { 0 }; /* rsp ring */
|
||
struct tqpkt tq_pkt[TQ_NPKTS]; /* packet queue */
|
||
int32 tq_freq = 0; /* free list */
|
||
int32 tq_rspq = 0; /* resp list */
|
||
uint32 tq_pbsy = 0; /* #busy pkts */
|
||
uint32 tq_credits = 0; /* credits */
|
||
uint32 tq_hat = 0; /* host timer */
|
||
uint32 tq_htmo = TQ_DHTMO; /* host timeout */
|
||
int32 tq_itime = 200; /* init time, except */
|
||
int32 tq_itime4 = 10; /* stage 4 */
|
||
int32 tq_qtime = 200; /* queue time */
|
||
int32 tq_xtime = 500; /* transfer time */
|
||
int32 tq_typ = 0; /* device type */
|
||
|
||
/* Command table - legal modifiers (low 16b) and flags (high 16b) */
|
||
|
||
static uint32 tq_cmf[64] = {
|
||
0, /* 0 */
|
||
CMF_IMM, /* abort */
|
||
CMF_IMM, /* get cmd status */
|
||
CMF_IMM|MD_NXU, /* get unit status */
|
||
CMF_IMM, /* set ctrl char */
|
||
0, 0, 0, /* 5-7 */
|
||
CMF_SEQ|MD_ACL|MD_CDL|MD_CSE|MD_EXA|MD_UNL, /* available */
|
||
CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* online */
|
||
CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* set unit char */
|
||
CMF_IMM, /* define acc paths */
|
||
0, 0, 0, 0, /* 12-15 */
|
||
CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* access */
|
||
MD_SCH|MD_SEC|MD_SER,
|
||
0, /* 17 */
|
||
CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase */
|
||
CMF_SEQ|CMF_WR|MD_CDL|MD_CSE, /* flush */
|
||
0, 0, /* 20-21 */
|
||
CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase gap */
|
||
0, 0, 0, 0, 0, 0, 0, 0, 0, /* 22-31 */
|
||
CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* compare */
|
||
MD_SCH|MD_SEC|MD_SER,
|
||
CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV|MD_CMP| /* read */
|
||
MD_SCH|MD_SEC|MD_SER,
|
||
CMF_SEQ|CMF_RW|CMF_WR|MD_CDL|MD_CSE|MD_IMM| /* write */
|
||
MD_CMP|MD_ERW|MD_SEC|MD_SER,
|
||
0, /* 35 */
|
||
CMF_SEQ|MD_CDL|MD_CSE|MD_IMM, /* wr tape mark */
|
||
CMF_SEQ|MD_CDL|MD_CSE|MD_IMM|MD_OBC| /* reposition */
|
||
MD_REV|MD_RWD|MD_DLE|
|
||
MD_SCH|MD_SEC|MD_SER,
|
||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 38-47 */
|
||
0, 0, 0, 0, 0, 0, 0, 0,
|
||
0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
||
/* Forward references */
|
||
|
||
DEVICE tq_dev;
|
||
|
||
t_stat tq_rd (int32 *data, int32 PA, int32 access);
|
||
t_stat tq_wr (int32 data, int32 PA, int32 access);
|
||
t_stat tq_inta (void);
|
||
t_stat tq_svc (UNIT *uptr);
|
||
t_stat tq_tmrsvc (UNIT *uptr);
|
||
t_stat tq_quesvc (UNIT *uptr);
|
||
t_stat tq_reset (DEVICE *dptr);
|
||
t_stat tq_attach (UNIT *uptr, char *cptr);
|
||
t_stat tq_detach (UNIT *uptr);
|
||
t_stat tq_boot (int32 unitno, DEVICE *dptr);
|
||
t_stat tq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
t_stat tq_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
t_stat tq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
t_stat tq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
|
||
t_bool tq_step4 (void);
|
||
t_bool tq_mscp (int32 pkt, t_bool q);
|
||
t_bool tq_abo (int32 pkt);
|
||
t_bool tq_avl (int32 pkt);
|
||
t_bool tq_erase (int32 pkt);
|
||
t_bool tq_flu (int32 pkt);
|
||
t_bool tq_gcs (int32 pkt);
|
||
t_bool tq_gus (int32 pkt);
|
||
t_bool tq_onl (int32 pkt);
|
||
t_bool tq_pos (int32 pkt);
|
||
t_bool tq_rw (int32 pkt);
|
||
t_bool tq_scc (int32 pkt);
|
||
t_bool tq_suc (int32 pkt);
|
||
t_bool tq_wtm (int32 pkt);
|
||
t_bool tq_plf (uint32 err);
|
||
t_bool tq_dte (UNIT *uptr, uint32 err);
|
||
t_bool tq_hbe (UNIT *uptr, uint32 ba);
|
||
t_bool tq_una (UNIT *uptr);
|
||
uint32 tq_rdlntf (UNIT *uptr, t_mtrlnt *tbc);
|
||
uint32 tq_spacef (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec);
|
||
uint32 tq_skipff (UNIT *uptr, uint32 cnt, uint32 *skipped);
|
||
uint32 tq_rdbuff (UNIT *uptr, t_mtrlnt *tbc);
|
||
uint32 tq_rdlntr (UNIT *uptr, t_mtrlnt *tbc);
|
||
uint32 tq_spacer (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec);
|
||
uint32 tq_skipfr (UNIT *uptr, uint32 cnt, uint32 *skipped);
|
||
uint32 tq_rdbufr (UNIT *uptr, t_mtrlnt *tbc);
|
||
t_bool tq_deqf (int32 *pkt);
|
||
int32 tq_deqh (int32 *lh);
|
||
void tq_enqh (int32 *lh, int32 pkt);
|
||
void tq_enqt (int32 *lh, int32 pkt);
|
||
t_bool tq_getpkt (int32 *pkt);
|
||
t_bool tq_putpkt (int32 pkt, t_bool qt);
|
||
t_bool tq_getdesc (struct uq_ring *ring, uint32 *desc);
|
||
t_bool tq_putdesc (struct uq_ring *ring, uint32 desc);
|
||
int32 tq_mot_valid (UNIT *uptr, uint32 cmd);
|
||
t_stat tq_mot_err (UNIT *uptr, uint32 rsiz);
|
||
t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint32 sts, uint32 rsiz);
|
||
void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ);
|
||
void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all);
|
||
void tq_setf_unit (int32 pkt, UNIT *uptr);
|
||
uint32 tq_efl (UNIT *uptr);
|
||
void tq_init_int (void);
|
||
void tq_ring_int (struct uq_ring *ring);
|
||
t_bool tq_fatal (uint32 err);
|
||
UNIT *tq_getucb (uint32 lu);
|
||
|
||
/* TQ data structures
|
||
|
||
tq_dev TQ device descriptor
|
||
tq_unit TQ unit list
|
||
tq_reg TQ register list
|
||
tq_mod TQ modifier list
|
||
*/
|
||
|
||
DIB tq_dib = { IOBA_TQ, IOLN_TQ, &tq_rd, &tq_wr,
|
||
1, IVCL (TQ), 0, { &tq_inta } };
|
||
|
||
UNIT tq_unit[] = {
|
||
{ UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), 0 },
|
||
{ UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), 0 },
|
||
{ UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), 0 },
|
||
{ UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), 0 },
|
||
{ UDATA (&tq_tmrsvc, UNIT_DIS, 0) },
|
||
{ UDATA (&tq_quesvc, UNIT_DIS, 0) } };
|
||
|
||
#define TQ_TIMER (TQ_NUMDR)
|
||
#define TQ_QUEUE (TQ_TIMER + 1)
|
||
|
||
REG tq_reg[] = {
|
||
{ GRDATA (SA, tq_sa, TQ_RDX, 16, 0) },
|
||
{ GRDATA (SAW, tq_saw, TQ_RDX, 16, 0) },
|
||
{ GRDATA (S1DAT, tq_s1dat, TQ_RDX, 16, 0) },
|
||
{ GRDATA (CQBA, tq_cq.ba, TQ_RDX, 22, 0) },
|
||
{ GRDATA (CQLNT, tq_cq.lnt, TQ_RDX, 8, 2), REG_NZ },
|
||
{ GRDATA (CQIDX, tq_cq.idx, TQ_RDX, 8, 2) },
|
||
{ GRDATA (TQBA, tq_rq.ba, TQ_RDX, 22, 0) },
|
||
{ GRDATA (TQLNT, tq_rq.lnt, TQ_RDX, 8, 2), REG_NZ },
|
||
{ GRDATA (TQIDX, tq_rq.idx, TQ_RDX, 8, 2) },
|
||
{ DRDATA (FREE, tq_freq, 5) },
|
||
{ DRDATA (RESP, tq_rspq, 5) },
|
||
{ DRDATA (PBSY, tq_pbsy, 5) },
|
||
{ GRDATA (CFLGS, tq_cflgs, TQ_RDX, 16, 0) },
|
||
{ GRDATA (CSTA, tq_csta, TQ_RDX, 4, 0) },
|
||
{ GRDATA (PERR, tq_perr, TQ_RDX, 9, 0) },
|
||
{ DRDATA (CRED, tq_credits, 5) },
|
||
{ DRDATA (HAT, tq_hat, 17) },
|
||
{ DRDATA (HTMO, tq_htmo, 17) },
|
||
{ URDATA (CPKT, tq_unit[0].cpkt, 10, 5, 0, TQ_NUMDR, 0) },
|
||
{ URDATA (PKTQ, tq_unit[0].pktq, 10, 5, 0, TQ_NUMDR, 0) },
|
||
{ URDATA (UFLG, tq_unit[0].uf, TQ_RDX, 16, 0, TQ_NUMDR, 0) },
|
||
{ URDATA (POS, tq_unit[0].pos, 10, 32, 0, TQ_NUMDR, 0) },
|
||
{ URDATA (OBJP, tq_unit[0].objp, 10, 32, 0, TQ_NUMDR, 0) },
|
||
{ FLDATA (PRGI, tq_prgi, 0), REG_HIDDEN },
|
||
{ FLDATA (PIP, tq_pip, 0), REG_HIDDEN },
|
||
{ FLDATA (INT, IREQ (TQ), INT_V_TQ) },
|
||
{ DRDATA (ITIME, tq_itime, 24), PV_LEFT + REG_NZ },
|
||
{ DRDATA (I4TIME, tq_itime4, 24), PV_LEFT + REG_NZ },
|
||
{ DRDATA (QTIME, tq_qtime, 24), PV_LEFT + REG_NZ },
|
||
{ DRDATA (XTIME, tq_xtime, 24), PV_LEFT + REG_NZ },
|
||
{ BRDATA (PKTS, tq_pkt, TQ_RDX, 16, TQ_NPKTS * (TQ_PKT_SIZE_W + 1)) },
|
||
{ FLDATA (DEVTYPE, tq_typ, 0), REG_HRO },
|
||
{ GRDATA (DEVADDR, tq_dib.ba, TQ_RDX, 32, 0), REG_HRO },
|
||
{ GRDATA (DEVVEC, tq_dib.vec, TQ_RDX, 16, 0), REG_HRO },
|
||
{ NULL } };
|
||
|
||
MTAB tq_mod[] = {
|
||
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
|
||
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_RI, "RINGS", NULL,
|
||
NULL, &tq_show_ctrl, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_FR, "FREEQ", NULL,
|
||
NULL, &tq_show_ctrl, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_RS, "RESPQ", NULL,
|
||
NULL, &tq_show_ctrl, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_UN, "UNITQ", NULL,
|
||
NULL, &tq_show_ctrl, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, -1, "ALL", NULL,
|
||
NULL, &tq_show_ctrl, NULL },
|
||
{ MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "UNITQ", NULL,
|
||
NULL, &tq_show_unitq, 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 },
|
||
{ 0 } };
|
||
|
||
DEVICE tq_dev = {
|
||
"TQ", tq_unit, tq_reg, tq_mod,
|
||
TQ_NUMDR + 2, 10, 31, 1, TQ_RDX, 8,
|
||
NULL, NULL, &tq_reset,
|
||
&tq_boot, &tq_attach, &tq_detach,
|
||
&tq_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS };
|
||
|
||
/* I/O dispatch routine, I/O addresses 17772150 - 17772152
|
||
|
||
17772150 IP read/write
|
||
17772152 SA read/write
|
||
*/
|
||
|
||
t_stat tq_rd (int32 *data, int32 PA, int32 access)
|
||
{
|
||
switch ((PA >> 1) & 01) { /* decode PA<1> */
|
||
case 0: /* IP */
|
||
*data = 0; /* reads zero */
|
||
if (tq_csta == CST_S3_PPB) tq_step4 (); /* waiting for poll? */
|
||
else if (tq_csta == CST_UP) { /* if up */
|
||
tq_pip = 1; /* poll host */
|
||
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); }
|
||
break;
|
||
case 1: /* SA */
|
||
*data = tq_sa;
|
||
break; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat tq_wr (int32 data, int32 PA, int32 access)
|
||
{
|
||
switch ((PA >> 1) & 01) { /* decode PA<1> */
|
||
case 0: /* IP */
|
||
tq_reset (&tq_dev); /* init device */
|
||
if (DBG_LOG (LOG_TQ)) fprintf (sim_log, ">>TQ: initialization started\n");
|
||
break;
|
||
case 1: /* SA */
|
||
tq_saw = data;
|
||
if (tq_csta < CST_S4) /* stages 1-3 */
|
||
sim_activate (&tq_unit[TQ_QUEUE], tq_itime);
|
||
else if (tq_csta == CST_S4) /* stage 4 (fast) */
|
||
sim_activate (&tq_unit[TQ_QUEUE], tq_itime4);
|
||
break; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Transition to step 4 - init communications region */
|
||
|
||
t_bool tq_step4 (void)
|
||
{
|
||
int32 i, lnt;
|
||
t_addr base;
|
||
uint16 zero[SA_COMM_MAX >> 1];
|
||
|
||
tq_rq.ioff = SA_COMM_RI; /* set intr offset */
|
||
tq_rq.ba = tq_comm; /* set rsp q base */
|
||
tq_rq.lnt = SA_S1H_RQ (tq_s1dat) << 2; /* get resp q len */
|
||
tq_cq.ioff = SA_COMM_CI; /* set intr offset */
|
||
tq_cq.ba = tq_comm + tq_rq.lnt; /* set cmd q base */
|
||
tq_cq.lnt = SA_S1H_CQ (tq_s1dat) << 2; /* get cmd q len */
|
||
tq_cq.idx = tq_rq.idx = 0; /* clear q idx's */
|
||
if (tq_prgi) base = tq_comm + SA_COMM_QQ;
|
||
else base = tq_comm + SA_COMM_CI;
|
||
lnt = tq_comm + tq_cq.lnt + tq_rq.lnt - base; /* comm lnt */
|
||
if (lnt > SA_COMM_MAX) 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 tq_fatal (PE_QWE); /* error? */
|
||
tq_sa = SA_S4 | (drv_tab[tq_typ].uqpm << SA_S4C_V_MOD) |/* send step 4 */
|
||
((drv_tab[tq_typ].cver & 0xFF) << SA_S4C_V_VER);
|
||
tq_csta = CST_S4; /* set step 4 */
|
||
tq_init_int (); /* poke host */
|
||
return OK;
|
||
}
|
||
|
||
/* Queue service - invoked when any of the queues (host queue, unit
|
||
queues, response queue) require servicing. Also invoked during
|
||
initialization to provide some delay to the next step.
|
||
|
||
Process at most one item off the host queue
|
||
If the host queue is empty, process at most one item off
|
||
each unit queue
|
||
Process at most one item off the response queue
|
||
|
||
If all queues are idle, terminate thread
|
||
*/
|
||
|
||
t_stat tq_quesvc (UNIT *uptr)
|
||
{
|
||
int32 i, cnid;
|
||
int32 pkt = 0;
|
||
UNIT *nuptr;
|
||
|
||
if (tq_csta < CST_UP) { /* still init? */
|
||
switch (tq_csta) { /* controller state? */
|
||
case CST_S1: /* need S1 reply */
|
||
if (tq_saw & SA_S1H_VL) { /* valid? */
|
||
if (tq_saw & SA_S1H_WR) { /* wrap? */
|
||
tq_sa = tq_saw; /* echo data */
|
||
tq_csta = CST_S1_WR; } /* endless loop */
|
||
else {
|
||
tq_s1dat = tq_saw; /* save data */
|
||
tq_dib.vec = VEC_Q + ((tq_s1dat & SA_S1H_VEC) << 2);
|
||
tq_sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (tq_s1dat);
|
||
tq_csta = CST_S2; /* now in step 2 */
|
||
tq_init_int (); } /* intr if req */
|
||
} /* end if valid */
|
||
break;
|
||
case CST_S1_WR: /* wrap mode */
|
||
tq_sa = tq_saw; /* echo data */
|
||
break;
|
||
case CST_S2: /* need S2 reply */
|
||
tq_comm = tq_saw & SA_S2H_CLO; /* get low addr */
|
||
tq_prgi = tq_saw & SA_S2H_PI; /* get purge int */
|
||
tq_sa = SA_S3 | SA_S3C_EC (tq_s1dat);
|
||
tq_csta = CST_S3; /* now in step 3 */
|
||
tq_init_int (); /* intr if req */
|
||
break;
|
||
case CST_S3: /* need S3 reply */
|
||
tq_comm = ((tq_saw & SA_S3H_CHI) << 16) | tq_comm;
|
||
if (tq_saw & SA_S3H_PP) { /* purge/poll test? */
|
||
tq_sa = 0; /* put 0 */
|
||
tq_csta = CST_S3_PPA; } /* wait for 0 write */
|
||
else tq_step4 (); /* send step 4 */
|
||
break;
|
||
case CST_S3_PPA: /* need purge test */
|
||
if (tq_saw) tq_fatal (PE_PPF); /* data not zero? */
|
||
else tq_csta = CST_S3_PPB; /* wait for poll */
|
||
break;
|
||
case CST_S4: /* need S4 reply */
|
||
if (tq_saw & SA_S4H_GO) { /* go set? */
|
||
if (DBG_LOG (LOG_TQ)) fprintf (sim_log,
|
||
">>TQ: initialization complete\n");
|
||
tq_csta = CST_UP; /* we're up */
|
||
tq_sa = 0; /* clear SA */
|
||
sim_activate (&tq_unit[TQ_TIMER], tmr_poll * clk_tps);
|
||
if ((tq_saw & SA_S4H_LF) && tq_perr) tq_plf (tq_perr);
|
||
tq_perr = 0; }
|
||
break; } /* end switch */
|
||
return SCPE_OK; } /* end if */
|
||
|
||
if (tq_pip) { /* polling? */
|
||
if (!tq_getpkt (&pkt)) return SCPE_OK; /* get host pkt */
|
||
if (pkt) { /* got one? */
|
||
if (DBG_LOG (LOG_TQ)) {
|
||
UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]);
|
||
fprintf (sim_log, ">>TQ: cmd=%04X, mod=%04X, unit=%d, ",
|
||
tq_pkt[pkt].d[CMD_OPC], tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN]);
|
||
fprintf (sim_log, "bc=%04X%04X, ma=%04X%04X",
|
||
tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL],
|
||
tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL]);
|
||
if (up) fprintf (sim_log, ", pos=%d, obj=%d\n", up->pos, up->objp);
|
||
else fprintf (sim_log, "\n"); }
|
||
if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */
|
||
return tq_fatal (PE_PIE); /* no, term thread */
|
||
cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */
|
||
if (cnid == UQ_CID_TMSCP) { /* TMSCP packet? */
|
||
if (!tq_mscp (pkt, TRUE)) return SCPE_OK; } /* proc, q non-seq */
|
||
else if (cnid == UQ_CID_DUP) { /* DUP packet? */
|
||
tq_putr (pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ);
|
||
if (!tq_putpkt (pkt, TRUE)) return SCPE_OK; } /* ill cmd */
|
||
else return tq_fatal (PE_ICI); /* no, term thread */
|
||
} /* end if pkt */
|
||
else tq_pip = 0; /* discontinue poll */
|
||
} /* end if pip */
|
||
if (!tq_pip) { /* not polling? */
|
||
for (i = 0; i < TQ_NUMDR; i++) { /* chk unit q's */
|
||
nuptr = tq_dev.units + i; /* ptr to unit */
|
||
if (nuptr->cpkt || (nuptr->pktq == 0)) continue;
|
||
pkt = tq_deqh (&nuptr->pktq); /* get top of q */
|
||
if (!tq_mscp (pkt, FALSE)) return SCPE_OK; } /* process */
|
||
} /* end if !pip */
|
||
if (tq_rspq) { /* resp q? */
|
||
pkt = tq_deqh (&tq_rspq); /* get top of q */
|
||
if (!tq_putpkt (pkt, FALSE)) return SCPE_OK; /* send to hst */
|
||
} /* end if resp q */
|
||
if (pkt) sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* more to do? */
|
||
return SCPE_OK; /* done */
|
||
}
|
||
|
||
/* Clock service (roughly once per second) */
|
||
|
||
t_stat tq_tmrsvc (UNIT *uptr)
|
||
{
|
||
int32 i;
|
||
UNIT *nuptr;
|
||
|
||
sim_activate (uptr, tmr_poll * clk_tps); /* reactivate */
|
||
for (i = 0; i < TQ_NUMDR; i++) { /* poll */
|
||
nuptr = tq_dev.units + i;
|
||
if ((nuptr->flags & UNIT_ATP) && /* ATN pending? */
|
||
(nuptr->flags & UNIT_ATT) && /* still online? */
|
||
(tq_cflgs & CF_ATN)) { /* wanted? */
|
||
if (!tq_una (nuptr)) return SCPE_OK; }
|
||
nuptr->flags = nuptr->flags & ~UNIT_ATP; }
|
||
if ((tq_hat > 0) && (--tq_hat == 0)) /* host timeout? */
|
||
tq_fatal (PE_HAT); /* fatal err */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* MSCP packet handling */
|
||
|
||
t_bool tq_mscp (int32 pkt, t_bool q)
|
||
{
|
||
uint32 sts;
|
||
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* command */
|
||
uint32 flg = GETP (pkt, CMD_OPC, FLG); /* flags */
|
||
uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
UNIT *uptr;
|
||
|
||
if ((cmd >= 64) || (tq_cmf[cmd] == 0)) { /* invalid cmd? */
|
||
cmd = OP_END; /* set end op */
|
||
sts = ST_CMD | I_OPCD; } /* ill op */
|
||
else if (flg) { /* flags? */
|
||
cmd = cmd | OP_END; /* set end flag */
|
||
sts = ST_CMD | I_FLAG; } /* ill flags */
|
||
else if (mdf & ~tq_cmf[cmd]) { /* invalid mod? */
|
||
cmd = cmd | OP_END; /* set end flag */
|
||
sts = ST_CMD | I_MODF; } /* ill mods */
|
||
else { /* valid cmd */
|
||
if (uptr = tq_getucb (lu)) { /* valid unit? */
|
||
if (q && uptr->cpkt && (tq_cmf[cmd] & CMF_SEQ)) {
|
||
tq_enqt (&uptr->pktq, pkt); /* do later */
|
||
return OK; }
|
||
/* if (tq_cmf[cmd] & MD_CDL) /* clr cch lost? */
|
||
/* uptr->flags = uptr->flags & ~UNIT_CDL; */
|
||
if (tq_cmf[cmd] & MD_CSE) /* clr ser exc? */
|
||
uptr->flags = uptr->flags & ~UNIT_SXC; }
|
||
switch (cmd) {
|
||
case OP_ABO: /* abort */
|
||
return tq_abo (pkt);
|
||
case OP_AVL: /* avail */
|
||
return tq_avl (pkt);
|
||
case OP_GCS: /* get cmd status */
|
||
return tq_gcs (pkt);
|
||
case OP_GUS: /* get unit status */
|
||
return tq_gus (pkt);
|
||
case OP_ONL: /* online */
|
||
return tq_onl (pkt);
|
||
case OP_SCC: /* set ctrl char */
|
||
return tq_scc (pkt);
|
||
case OP_SUC: /* set unit char */
|
||
return tq_suc (pkt);
|
||
case OP_ERS: /* erase */
|
||
case OP_ERG: /* erase gap */
|
||
return tq_erase (pkt);
|
||
case OP_FLU: /* flush */
|
||
return tq_flu (pkt);
|
||
case OP_POS: /* position */
|
||
return tq_pos (pkt);
|
||
case OP_WTM: /* write tape mark */
|
||
return tq_wtm (pkt);
|
||
case OP_ACC: /* access */
|
||
case OP_CMP: /* compare */
|
||
case OP_RD: /* read */
|
||
case OP_WR: /* write */
|
||
return tq_rw (pkt);
|
||
case OP_DAP:
|
||
cmd = cmd | OP_END; /* set end flag */
|
||
sts = ST_SUC; /* success */
|
||
break;
|
||
default:
|
||
cmd = OP_END; /* set end op */
|
||
sts = ST_CMD | I_OPCD; /* ill op */
|
||
break;
|
||
} /* end switch */
|
||
} /* end else */
|
||
tq_putr (pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Abort a command - 1st parameter is ref # of cmd to abort */
|
||
|
||
t_bool tq_abo (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */
|
||
int32 tpkt, prv;
|
||
UNIT *uptr;
|
||
|
||
tpkt = 0; /* set no mtch */
|
||
if (uptr = tq_getucb (lu)) { /* get unit */
|
||
if (uptr->cpkt && /* curr pkt? */
|
||
(GETP32 (uptr->cpkt, CMD_REFL) == ref)) { /* match ref? */
|
||
tpkt = uptr->cpkt; /* save match */
|
||
uptr->cpkt = 0; /* gonzo */
|
||
sim_cancel (uptr); /* cancel unit */
|
||
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); }
|
||
else if (uptr->pktq && /* head of q? */
|
||
(GETP32 (uptr->pktq, CMD_REFL) == ref)) { /* match ref? */
|
||
tpkt = uptr->pktq; /* save match */
|
||
uptr->pktq = tq_pkt[tpkt].link; } /* unlink */
|
||
else if (prv = uptr->pktq) { /* srch pkt q */
|
||
while (tpkt = tq_pkt[prv].link) { /* walk list */
|
||
if (GETP32 (tpkt, RSP_REFL) == ref) { /* match ref? */
|
||
tq_pkt[prv].link = tq_pkt[tpkt].link; /* unlink */
|
||
break; } } }
|
||
if (tpkt) { /* found target? */
|
||
uint32 tcmd = GETP (tpkt, CMD_OPC, OPC); /* get opcode */
|
||
tq_putr (tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ);
|
||
if (!tq_putpkt (tpkt, TRUE)) return ERR; }
|
||
} /* end if unit */
|
||
tq_putr (pkt, OP_ABO | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Unit available - set unit status to available
|
||
Deferred if q'd cmds, bypassed if ser exc */
|
||
|
||
t_bool tq_avl (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifiers */
|
||
uint32 sts;
|
||
UNIT *uptr;
|
||
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
if (uptr->flags & UNIT_SXC) sts = ST_SXC; /* ser exc pending? */
|
||
else {
|
||
uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_TMK | UNIT_POL);
|
||
uptr->uf = uptr->pos = uptr->objp = 0; /* clr flags, rewind */
|
||
if (uptr->flags & UNIT_ATT) { /* attached? */
|
||
sts = ST_SUC; /* success */
|
||
if (mdf & MD_UNL) tq_detach (uptr); } /* unload? */
|
||
else sts = ST_OFL | SB_OFL_NV; } } /* no, offline */
|
||
else sts = ST_OFL; /* offline */
|
||
tq_putr (pkt, OP_AVL | OP_END, tq_efl (uptr), sts, AVL_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Get command status - only interested in active xfr cmd */
|
||
|
||
t_bool tq_gcs (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */
|
||
int32 tpkt;
|
||
UNIT *uptr;
|
||
|
||
if ((uptr = tq_getucb (lu)) && /* valid lu? */
|
||
(tpkt = uptr->cpkt) && /* queued pkt? */
|
||
(GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */
|
||
(tq_cmf[GETP (tpkt, CMD_OPC, OPC)] & CMF_RW)) { /* rd/wr cmd? */
|
||
tq_pkt[pkt].d[GCS_STSL] = tq_pkt[tpkt].d[RW_BCL];
|
||
tq_pkt[pkt].d[GCS_STSH] = tq_pkt[tpkt].d[RW_BCH]; }
|
||
else tq_pkt[pkt].d[GCS_STSL] = tq_pkt[pkt].d[GCS_STSH] = 0;
|
||
tq_putr (pkt, OP_GCS | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Get unit status */
|
||
|
||
t_bool tq_gus (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 sts;
|
||
UNIT *uptr;
|
||
|
||
if (tq_pkt[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */
|
||
if (lu >= TQ_NUMDR) { /* end of range? */
|
||
lu = 0; /* reset to 0 */
|
||
tq_pkt[pkt].d[RSP_UN] = lu; } }
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
||
sts = ST_OFL | SB_OFL_NV; /* offl no vol */
|
||
else if (uptr->flags & UNIT_ONL) sts = ST_SUC; /* online */
|
||
else sts = ST_AVL; /* avail */
|
||
tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */
|
||
tq_pkt[pkt].d[GUS_MENU] = drv_tab[tq_typ].fmt; /* format menu */
|
||
tq_pkt[pkt].d[GUS_CAP] = 0; /* free capacity */
|
||
tq_pkt[pkt].d[GUS_FVER] = drv_tab[tq_typ].fver; /* formatter version */
|
||
tq_pkt[pkt].d[GUS_UVER] = drv_tab[tq_typ].uver; /* unit version */
|
||
}
|
||
else sts = ST_OFL; /* offline */
|
||
tq_putr (pkt, OP_GUS | OP_END, tq_efl (uptr), sts, GUS_LNT_T, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Unit online - deferred if q'd commands */
|
||
|
||
t_bool tq_onl (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 sts;
|
||
UNIT *uptr;
|
||
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
||
sts = ST_OFL | SB_OFL_NV; /* offl no vol */
|
||
else if (uptr->flags & UNIT_ONL) /* already online? */
|
||
sts = ST_SUC | SB_SUC_ON;
|
||
else { sts = ST_SUC; /* mark online */
|
||
uptr->pos = uptr->objp = 0; /* rewind */
|
||
uptr->flags = (uptr->flags | UNIT_ONL) &
|
||
~(UNIT_TMK | UNIT_POL); /* onl, pos ok */
|
||
tq_setf_unit (pkt, uptr); } /* hack flags */
|
||
tq_putr_unit (pkt, uptr, lu, TRUE); } /* set fields */
|
||
else sts = ST_OFL; /* offline */
|
||
tq_putr (pkt, OP_ONL | OP_END, tq_efl (uptr), sts, ONL_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Set controller characteristics */
|
||
|
||
t_bool tq_scc (int32 pkt)
|
||
{
|
||
if (tq_pkt[pkt].d[SCC_MSV]) /* MSCP ver = 0? */
|
||
tq_putr (pkt, 0, 0, ST_CMD | I_VRSN, SCC_LNT, UQ_TYP_SEQ);
|
||
else { tq_cflgs = (tq_cflgs & CF_RPL) | /* hack ctrl flgs */
|
||
tq_pkt[pkt].d[SCC_CFL];
|
||
if (tq_htmo = tq_pkt[pkt].d[SCC_TMO]) /* set timeout */
|
||
tq_htmo = tq_htmo + 2; /* if nz, round up */
|
||
tq_pkt[pkt].d[SCC_CFL] = tq_cflgs; /* return flags */
|
||
tq_pkt[pkt].d[SCC_TMO] = TQ_DCTMO; /* ctrl timeout */
|
||
tq_pkt[pkt].d[SCC_VER] = drv_tab[tq_typ].cver; /* ctrl version */
|
||
tq_pkt[pkt].d[SCC_CIDA] = 0; /* ctrl ID */
|
||
tq_pkt[pkt].d[SCC_CIDB] = 0;
|
||
tq_pkt[pkt].d[SCC_CIDC] = 0;
|
||
tq_pkt[pkt].d[SCC_CIDD] = (TQ_CLASS << SCC_CIDD_V_CLS) |
|
||
(drv_tab[tq_typ].cmod << SCC_CIDD_V_MOD);
|
||
PUTP32 (pkt, SCC_MBCL, TQ_MAXFR); /* max bc */
|
||
tq_putr (pkt, OP_SCC | OP_END, 0, ST_SUC, SCC_LNT, UQ_TYP_SEQ);
|
||
}
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Set unit characteristics - defer if q'd commands */
|
||
|
||
t_bool tq_suc (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 sts;
|
||
UNIT *uptr;
|
||
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
||
sts = ST_OFL | SB_OFL_NV; /* offl no vol */
|
||
else { sts = ST_SUC; /* avail or onl */
|
||
tq_setf_unit (pkt, uptr); } /* hack flags */
|
||
tq_putr_unit (pkt, uptr, lu, TRUE); } /* set fields */
|
||
else sts = ST_OFL; /* offline */
|
||
tq_putr (pkt, OP_SUC | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Flush - sequential nop - deferred if q'd cmds, bypassed if ser exc */
|
||
|
||
t_bool tq_flu (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 sts;
|
||
UNIT *uptr;
|
||
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
sts = tq_mot_valid (uptr, OP_FLU); } /* validate req */
|
||
else sts = ST_OFL; /* offline */
|
||
tq_putr (pkt, OP_FLU | OP_END, tq_efl (uptr), sts, FLU_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Erase, erase gap - deferred if q'd cmds, bypassed if ser exc */
|
||
|
||
t_bool tq_erase (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */
|
||
uint32 sts;
|
||
UNIT *uptr;
|
||
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
sts = tq_mot_valid (uptr, cmd); /* validity checks */
|
||
if (sts == ST_SUC) { /* ok? */
|
||
uptr->cpkt = pkt; /* op in progress */
|
||
sim_activate (uptr, tq_xtime); /* activate */
|
||
return OK; } } /* done */
|
||
else sts = ST_OFL; /* offline */
|
||
tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, ERS_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Write tape mark - deferred if q'd cmds, bypassed if ser exc */
|
||
|
||
t_bool tq_wtm (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 sts, objp = 0;
|
||
UNIT *uptr;
|
||
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
objp = uptr->objp; /* position op */
|
||
sts = tq_mot_valid (uptr, OP_WTM); /* validity checks */
|
||
if (sts == ST_SUC) { /* ok? */
|
||
uptr->cpkt = pkt; /* op in progress */
|
||
sim_activate (uptr, tq_xtime); /* activate */
|
||
return OK; } } /* done */
|
||
else sts = ST_OFL; /* offline */
|
||
PUTP32 (pkt, WTM_POSL, objp); /* set obj pos */
|
||
tq_putr (pkt, OP_WTM | OP_END, tq_efl (uptr), sts, WTM_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Position - deferred if q'd cmds, bypassed if ser exc */
|
||
|
||
t_bool tq_pos (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 sts, objp = 0;
|
||
UNIT *uptr;
|
||
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
objp = uptr->objp; /* position op */
|
||
sts = tq_mot_valid (uptr, OP_POS); /* validity checks */
|
||
if (sts == ST_SUC) { /* ok? */
|
||
uptr->cpkt = pkt; /* op in progress */
|
||
sim_activate (uptr, tq_xtime); /* activate */
|
||
return OK; } } /* done */
|
||
else sts = ST_OFL; /* offline */
|
||
PUTP32 (pkt, POS_RCL, 0); /* clear #skipped */
|
||
PUTP32 (pkt, POS_TMCL, 0);
|
||
PUTP32 (pkt, POS_POSL, objp); /* set obj pos */
|
||
tq_putr (pkt, OP_POS | OP_END, tq_efl (uptr), sts, POS_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Data transfer commands - deferred if q'd commands, bypassed if ser exc */
|
||
|
||
t_bool tq_rw (int32 pkt)
|
||
{
|
||
uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */
|
||
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */
|
||
uint32 bc = GETP32 (pkt, RW_BCL); /* byte count */
|
||
uint32 sts, objp = 0;
|
||
UNIT *uptr;
|
||
|
||
if (uptr = tq_getucb (lu)) { /* unit exist? */
|
||
objp = uptr->objp; /* position op */
|
||
sts = tq_mot_valid (uptr, cmd); /* validity checks */
|
||
if (sts == ST_SUC) { /* ok? */
|
||
if ((bc == 0) || (bc > TQ_MAXFR)) { /* invalid? */
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
||
sts = ST_CMD | I_BCNT; }
|
||
else {
|
||
uptr->cpkt = pkt; /* op in progress */
|
||
sim_activate (uptr, tq_xtime); /* activate */
|
||
return OK; } } } /* done */
|
||
else sts = ST_OFL; /* offline */
|
||
PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */
|
||
PUTP32 (pkt, RW_POSL, objp); /* set obj pos */
|
||
PUTP32 (pkt, RW_RSZL, 0); /* clr rec size */
|
||
tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, RW_LNT, UQ_TYP_SEQ);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Validity checks */
|
||
|
||
int32 tq_mot_valid (UNIT *uptr, uint32 cmd)
|
||
{
|
||
if (uptr->flags & UNIT_SXC) return ST_SXC; /* ser exc pend? */
|
||
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
||
return (ST_OFL | SB_OFL_NV); /* offl no vol */
|
||
if ((uptr->flags & UNIT_ONL) == 0) /* not online? */
|
||
return ST_AVL; /* only avail */
|
||
if (tq_cmf[cmd] & CMF_WR) { /* write op? */
|
||
if (uptr->uf & UF_WPS) { /* swre wlk? */
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
||
return (ST_WPR | SB_WPR_SW); }
|
||
if (TQ_WPH (uptr)) { /* hwre wlk? */
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
||
return (ST_WPR | SB_WPR_HW); } }
|
||
return ST_SUC; /* success! */
|
||
}
|
||
|
||
/* Unit service for motion commands */
|
||
|
||
t_stat tq_svc (UNIT *uptr)
|
||
{
|
||
uint32 t, sts, sktmk, skrec;
|
||
t_mtrlnt i, ebc, tbc, wbc;
|
||
int32 pkt = uptr->cpkt; /* get packet */
|
||
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */
|
||
uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */
|
||
uint32 ba = GETP32 (pkt, RW_BAL); /* buf addr */
|
||
t_mtrlnt bc = GETP32 (pkt, RW_BCL); /* byte count */
|
||
uint32 nrec = GETP32 (pkt, POS_RCL); /* #rec to skip */
|
||
uint32 ntmk = GETP32 (pkt, POS_TMCL); /* #tmk to skp */
|
||
static t_mtrlnt bceof = MTR_TMK;
|
||
static t_mtrlnt bceom = MTR_EOM;
|
||
|
||
if (pkt == 0) return SCPE_IERR; /* what??? */
|
||
if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
|
||
tq_mot_end (uptr, 0, ST_OFL | SB_OFL_NV, 0); /* offl no vol */
|
||
return SCPE_OK; }
|
||
|
||
if (tq_cmf[cmd] & CMF_WR) { /* write op? */
|
||
if (TQ_WPH (uptr)) { /* hwre write prot? */
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
||
tq_mot_end (uptr, 0, ST_WPR | SB_WPR_HW, 0);
|
||
return SCPE_OK; }
|
||
if (uptr->uf & UF_WPS) { /* swre write prot? */
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */
|
||
tq_mot_end (uptr, 0, ST_WPR | SB_WPR_SW, 0);
|
||
return SCPE_OK; } }
|
||
sts = ST_SUC; /* assume success */
|
||
tbc = 0; /* assume zero rec */
|
||
switch (cmd) { /* case on command */
|
||
|
||
case OP_RD:case OP_ACC:case OP_CMP: /* read-like op */
|
||
if (mdf & MD_REV) sts = tq_rdbufr (uptr, &tbc); /* read record */
|
||
else sts = tq_rdbuff (uptr, &tbc);
|
||
if (sts == ST_DRV) { /* read error? */
|
||
PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */
|
||
return tq_mot_err (uptr, tbc); } /* log, done */
|
||
if ((sts != ST_SUC) || (cmd == OP_ACC)) { /* error or access? */
|
||
PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */
|
||
break; }
|
||
if (tbc > bc) { /* tape rec > buf? */
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */
|
||
sts = ST_RDT; /* data truncated */
|
||
wbc = bc; } /* set working bc */
|
||
else wbc = tbc;
|
||
if (cmd == OP_RD) { /* read? */
|
||
if (t = Map_WriteB (ba, wbc, tqxb, MAP)) { /* store, nxm? */
|
||
PUTP32 (pkt, RW_BCL, wbc - t); /* adj bc */
|
||
if (tq_hbe (uptr, ba + wbc - t)) /* post err log */
|
||
tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, tbc);
|
||
return SCPE_OK; } /* end if nxm */
|
||
} /* end if read */
|
||
else { /* compare */
|
||
uint8 mby, dby;
|
||
uint32 mba;
|
||
for (i = 0; i < wbc; i++) { /* loop */
|
||
if (mdf & MD_REV) { /* reverse? */
|
||
mba = ba + bc - 1 - i; /* mem addr */
|
||
dby = tqxb[tbc - 1 - i]; } /* byte */
|
||
else {
|
||
mba = ba + i;
|
||
dby = tqxb[i]; }
|
||
if (Map_ReadB (mba, 1, &mby, MAP)) { /* fetch, nxm? */
|
||
PUTP32 (pkt, RW_BCL, i); /* adj bc */
|
||
if (tq_hbe (uptr, mba)) /* post err log */
|
||
tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, tbc);
|
||
return SCPE_OK; }
|
||
if (mby != dby) { /* cmp err? */
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* ser exc */
|
||
PUTP32 (pkt, RW_BCL, i); /* adj bc */
|
||
tq_mot_end (uptr, 0, ST_CMP, tbc);
|
||
return SCPE_OK; } /* exit */
|
||
} /* end for */
|
||
} /* end if compare */
|
||
PUTP32 (pkt, RW_BCL, wbc); /* bytes read/cmp'd */
|
||
break;
|
||
|
||
case OP_WR: /* write */
|
||
if (t = Map_ReadB (ba, bc, tqxb, MAP)) { /* fetch buf, nxm? */
|
||
PUTP32 (pkt, RW_BCL, 0); /* no bytes xfer'd */
|
||
if (tq_hbe (uptr, ba + bc - t)) /* post err log */
|
||
tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, bc);
|
||
return SCPE_OK; } /* end else wr */
|
||
ebc = (bc + 1) & ~1; /* force even */
|
||
fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */
|
||
fxwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
|
||
fxwrite (tqxb, sizeof (uint8), ebc, uptr->fileref);
|
||
fxwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref);
|
||
if (ferror (uptr->fileref)) /* error? */
|
||
return tq_mot_err (uptr, bc); /* log, end */
|
||
uptr->pos = uptr->pos + ebc + (2 * sizeof (t_mtrlnt));
|
||
uptr->objp = uptr->objp + 1; /* upd obj pos */
|
||
if (TEST_EOT (uptr)) /* EOT on write? */
|
||
uptr->flags = uptr->flags | UNIT_SXC;
|
||
uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */
|
||
tbc = bc; /* RW_BC is ok */
|
||
break;
|
||
|
||
case OP_WTM: /* write tape mark */
|
||
fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */
|
||
fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr->fileref);
|
||
if (ferror (uptr->fileref)) /* error? */
|
||
return tq_mot_err (uptr, 0); /* log, end */
|
||
uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* update pos */
|
||
uptr->objp = uptr->objp + 1; /* incr obj cnt */
|
||
case OP_ERG: /* erase gap */
|
||
if (TEST_EOT (uptr)) /* EOT on write? */
|
||
uptr->flags = uptr->flags | UNIT_SXC;
|
||
uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */
|
||
break;
|
||
|
||
case OP_ERS: /* erase */
|
||
fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */
|
||
fxwrite (&bceom, sizeof (t_mtrlnt), 1, uptr->fileref);
|
||
if (ferror (uptr->fileref)) /* error? */
|
||
return tq_mot_err (uptr, 0);
|
||
uptr->pos = uptr->objp = 0; /* rewind */
|
||
uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL);
|
||
break;
|
||
|
||
case OP_POS: /* position */
|
||
sktmk = skrec = 0; /* clr skipped */
|
||
if (mdf & MD_RWD) { /* rewind? */
|
||
uptr->pos = uptr->objp = 0; /* clr flags */
|
||
uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); }
|
||
if (mdf & MD_OBC) { /* skip obj? */
|
||
if (mdf & MD_REV) /* reverse? */
|
||
sts = tq_spacer (uptr, nrec, &skrec, FALSE);
|
||
else sts = tq_spacef (uptr, nrec, &skrec, FALSE);
|
||
}
|
||
else { /* skip tmk, rec */
|
||
if (mdf & MD_REV) sts = tq_skipfr (uptr, ntmk, &sktmk);
|
||
else sts = tq_skipff (uptr, ntmk, &sktmk);
|
||
if (sts == ST_SUC) { /* tmk succeed? */
|
||
if (mdf & MD_REV) /* reverse? */
|
||
sts = tq_spacer (uptr, nrec, &skrec, TRUE);
|
||
else sts = tq_spacef (uptr, nrec, &skrec, TRUE);
|
||
if (sts == ST_TMK) sktmk = sktmk + 1; }
|
||
}
|
||
PUTP32 (pkt, POS_RCL, skrec); /* #rec skipped */
|
||
PUTP32 (pkt, POS_TMCL, sktmk); /* #tmk skipped */
|
||
break;
|
||
|
||
default:
|
||
return SCPE_IERR; }
|
||
tq_mot_end (uptr, 0, sts, tbc); /* done */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Motion command drive error */
|
||
|
||
t_stat tq_mot_err (UNIT *uptr, uint32 rsiz)
|
||
{
|
||
uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_TMK; /* serious exception */
|
||
if (tq_dte (uptr, ST_DRV)) /* post err log */
|
||
tq_mot_end (uptr, EF_LOG, ST_DRV, rsiz); /* if ok, report err */
|
||
perror ("TQ I/O error");
|
||
clearerr (uptr->fileref);
|
||
return SCPE_IOERR;
|
||
}
|
||
|
||
/* Motion command complete */
|
||
|
||
t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint32 sts, uint32 rsiz)
|
||
{
|
||
int32 pkt = uptr->cpkt; /* packet */
|
||
uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */
|
||
uint32 lnt = RW_LNT; /* assume rw */
|
||
|
||
if (cmd == OP_ERG) lnt = ERG_LNT; /* set pkt lnt */
|
||
else if (cmd == OP_ERS) lnt = ERS_LNT;
|
||
else if (cmd == OP_WTM) lnt = WTM_LNT;
|
||
else if (cmd == OP_POS) lnt = POS_LNT;
|
||
|
||
uptr->cpkt = 0; /* done */
|
||
if (lnt > ERG_LNT) { /* xfer cmd? */
|
||
PUTP32 (pkt, RW_POSL, uptr->objp); } /* position */
|
||
if (lnt > POS_LNT) { /* read or write? */
|
||
PUTP32 (pkt, RW_RSZL, rsiz); } /* record size */
|
||
tq_putr (pkt, cmd | OP_END, flg | tq_efl (uptr), sts, lnt, UQ_TYP_SEQ);
|
||
if (!tq_putpkt (pkt, TRUE)) return ERR; /* send pkt */
|
||
if (uptr->pktq) /* more to do? */
|
||
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* activate thread */
|
||
return OK;
|
||
}
|
||
|
||
/* Tape motion routines */
|
||
|
||
uint32 tq_rdlntf (UNIT *uptr, t_mtrlnt *tbc)
|
||
{
|
||
fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */
|
||
fxread (tbc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */
|
||
if (ferror (uptr->fileref)) { /* error? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; /* ser exc, pos lost */
|
||
return ST_DRV; }
|
||
if (feof (uptr->fileref) || (*tbc == MTR_EOM)) { /* end of medium? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; /* ser exc, pos lost */
|
||
return ST_DAT; }
|
||
return 0;
|
||
}
|
||
|
||
uint32 tq_spacef (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec)
|
||
{
|
||
uint32 st;
|
||
t_mtrlnt tbc;
|
||
|
||
*skipped = 0;
|
||
while (*skipped < cnt) { /* loop */
|
||
if (st = tq_rdlntf (uptr, &tbc)) return st; /* read rec lnt */
|
||
uptr->objp = uptr->objp + 1; /* upd obj cnt */
|
||
if (tbc == MTR_TMK) { /* tape mark? */
|
||
int32 pkt = uptr->cpkt; /* get pkt */
|
||
if ((tq_pkt[pkt].d[CMD_MOD] & MD_DLE) && /* LEOT? */
|
||
(uptr->flags & UNIT_TMK)) {
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */
|
||
return ST_LED; } /* dont upd pos */
|
||
uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* update pos */
|
||
uptr->flags = uptr->flags | UNIT_TMK; /* set TM seen */
|
||
if (qrec) return ST_TMK; } /* rec spc? stop */
|
||
else {
|
||
uptr->pos = uptr->pos + ((MTRL (tbc) + 1) & ~1) +
|
||
(2 * sizeof (t_mtrlnt)); /* update pos */
|
||
uptr->flags = uptr->flags & ~UNIT_TMK; } /* clr TM seen */
|
||
*skipped = *skipped + 1; } /* # rec skipped */
|
||
return 0;
|
||
}
|
||
|
||
uint32 tq_skipff (UNIT *uptr, uint32 cnt, uint32 *skipped)
|
||
{
|
||
uint32 st, skrec;
|
||
|
||
*skipped = 0;
|
||
while (*skipped < cnt) { /* loop */
|
||
st = tq_spacef (uptr, 0xFFFFFFFF, &skrec, TRUE); /* rec spc fwd */
|
||
if (st == ST_TMK) *skipped = *skipped + 1; /* count files */
|
||
else if (st != 0) return st; }
|
||
return 0;
|
||
}
|
||
|
||
uint32 tq_rdlntr (UNIT *uptr, t_mtrlnt *tbc)
|
||
{
|
||
uptr->flags = uptr->flags & ~UNIT_TMK; /* rev clrs tmk */
|
||
if (uptr->pos < sizeof (t_mtrlnt)) { /* at BOT? */
|
||
uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_POL;
|
||
return ST_BOT; }
|
||
fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET);
|
||
fxread (tbc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */
|
||
if (ferror (uptr->fileref)) { /* error? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; /* ser exc, pos lost */
|
||
return ST_DRV; }
|
||
if (feof (uptr->fileref)) { /* end of file? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; /* ser exc, pos lost */
|
||
return ST_DAT; }
|
||
if (*tbc == MTR_EOM) { /* end of medium? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; /* ser exc, pos lost */
|
||
uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* spc over eom */
|
||
return ST_DAT; }
|
||
return 0;
|
||
}
|
||
|
||
uint32 tq_spacer (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec)
|
||
{
|
||
uint32 st;
|
||
t_mtrlnt tbc;
|
||
|
||
*skipped = 0;
|
||
while (*skipped < cnt) { /* loop */
|
||
if (st = tq_rdlntr (uptr, &tbc)) return st; /* read rec lnt */
|
||
uptr->objp = uptr->objp - 1; /* upd obj cnt */
|
||
if (tbc == MTR_TMK) { /* tape mark? */
|
||
uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* update pos */
|
||
if (qrec) return ST_TMK; } /* recd spc? stop */
|
||
else uptr->pos = uptr->pos - ((MTRL (tbc) + 1) & ~1) -
|
||
(2 * sizeof (t_mtrlnt)); /* update pos */
|
||
*skipped = *skipped + 1; } /* # rec skipped */
|
||
return 0;
|
||
}
|
||
|
||
uint32 tq_skipfr (UNIT *uptr, uint32 cnt, uint32 *skipped)
|
||
{
|
||
uint32 st, skrec;
|
||
|
||
*skipped = 0;
|
||
while (*skipped < cnt) { /* loopo */
|
||
st = tq_spacer (uptr, 0xFFFFFFFF, &skrec, TRUE); /* rec spc rev */
|
||
if (st == ST_TMK) *skipped = *skipped + 1; /* tape mark? */
|
||
else if (st != 0) return st; } /* error? */
|
||
return 0;
|
||
}
|
||
|
||
/* Read buffer - can return ST_TMK, ST_FMT, or ST_DRV */
|
||
|
||
uint32 tq_rdbuff (UNIT *uptr, t_mtrlnt *tbc)
|
||
{
|
||
t_mtrlnt i;
|
||
uint32 st;
|
||
|
||
if (st = tq_rdlntf (uptr, tbc)) return st; /* read rec lnt */
|
||
if (*tbc == MTR_TMK) { /* tape mark? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_TMK; /* serious exc */
|
||
uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* update pos */
|
||
uptr->objp = uptr->objp + 1; /* update obj cnt */
|
||
return ST_TMK; }
|
||
uptr->flags = uptr->flags & ~UNIT_TMK; /* clr tape mark */
|
||
*tbc = MTRL (*tbc); /* ignore err flag */
|
||
if (*tbc > MT_MAXFR) { /* rec out of range? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL;
|
||
return ST_FMT; }
|
||
i = fxread (tqxb, sizeof (uint8), *tbc, uptr->fileref); /* read record */
|
||
if (ferror (uptr->fileref)) { /* error? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL;
|
||
return ST_DRV; }
|
||
for ( ; i < *tbc; i++) tqxb[i] = 0; /* fill with 0's */
|
||
uptr->pos = uptr->pos + ((*tbc + 1) & ~1) + (2 * sizeof (t_mtrlnt));
|
||
uptr->objp = uptr->objp + 1; /* upd obj cnt */
|
||
return 0;
|
||
}
|
||
|
||
uint32 tq_rdbufr (UNIT *uptr, t_mtrlnt *tbc)
|
||
{
|
||
t_mtrlnt i;
|
||
uint32 st;
|
||
|
||
if (st = tq_rdlntr (uptr, tbc)) return st; /* read rec lnt */
|
||
if (*tbc == MTR_TMK) { /* tape mark? */
|
||
uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */
|
||
uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* update pos */
|
||
uptr->objp = uptr->objp - 1; /* update obj cnt */
|
||
return ST_TMK; }
|
||
*tbc = MTRL (*tbc); /* ignore err flag */
|
||
if (*tbc > MT_MAXFR) { /* rec out of range? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL;
|
||
return ST_FMT; }
|
||
fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt) - *tbc, SEEK_SET);
|
||
i = fxread (tqxb, sizeof (uint8), *tbc, uptr->fileref); /* read record */
|
||
if (ferror (uptr->fileref)) { /* error? */
|
||
uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL;
|
||
return ST_DRV; }
|
||
for ( ; i < *tbc; i++) tqxb[i] = 0; /* fill with 0's */
|
||
uptr->pos = uptr->pos - ((*tbc + 1) & ~1) - (2 * sizeof (t_mtrlnt));
|
||
uptr->objp = uptr->objp - 1; /* upd obj cnt */
|
||
return 0;
|
||
}
|
||
|
||
/* Data transfer error log packet */
|
||
|
||
t_bool tq_dte (UNIT *uptr, uint32 err)
|
||
{
|
||
int32 pkt, tpkt;
|
||
uint32 lu;
|
||
|
||
if ((tq_cflgs & CF_THS) == 0) return OK; /* logging? */
|
||
if (!tq_deqf (&pkt)) return ERR; /* get log pkt */
|
||
tpkt = uptr->cpkt; /* rw pkt */
|
||
lu = tq_pkt[tpkt].d[CMD_UN]; /* unit # */
|
||
|
||
tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */
|
||
tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */
|
||
tq_pkt[pkt].d[ELP_UN] = lu; /* copy unit */
|
||
tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */
|
||
tq_pkt[pkt].d[DTE_CIDA] = 0; /* ctrl ID */
|
||
tq_pkt[pkt].d[DTE_CIDB] = 0;
|
||
tq_pkt[pkt].d[DTE_CIDC] = 0;
|
||
tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) |
|
||
(drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD);
|
||
tq_pkt[pkt].d[DTE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */
|
||
tq_pkt[pkt].d[DTE_MLUN] = lu; /* MLUN */
|
||
tq_pkt[pkt].d[DTE_UIDA] = lu; /* unit ID */
|
||
tq_pkt[pkt].d[DTE_UIDB] = 0;
|
||
tq_pkt[pkt].d[DTE_UIDC] = 0;
|
||
tq_pkt[pkt].d[DTE_UIDD] = (UID_TAPE << DTE_UIDD_V_CLS) |
|
||
(drv_tab[tq_typ].umod << DTE_UIDD_V_MOD);
|
||
tq_pkt[pkt].d[DTE_UVER] = drv_tab[tq_typ].uver; /* unit ver */
|
||
PUTP32 (pkt, DTE_POSL, uptr->objp); /* position */
|
||
tq_pkt[pkt].d[DTE_FVER] = drv_tab[tq_typ].fver; /* fmtr ver */
|
||
tq_putr (pkt, FM_TAP, LF_SNR, err, DTE_LNT, UQ_TYP_DAT);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Host bus error log packet */
|
||
|
||
t_bool tq_hbe (UNIT *uptr, uint32 ba)
|
||
{
|
||
int32 pkt, tpkt;
|
||
|
||
if ((tq_cflgs & CF_THS) == 0) return OK; /* logging? */
|
||
if (!tq_deqf (&pkt)) return ERR; /* get log pkt */
|
||
tpkt = uptr->cpkt; /* rw pkt */
|
||
tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */
|
||
tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */
|
||
tq_pkt[pkt].d[ELP_UN] = tq_pkt[tpkt].d[CMD_UN]; /* copy unit */
|
||
tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */
|
||
tq_pkt[pkt].d[HBE_CIDA] = 0; /* ctrl ID */
|
||
tq_pkt[pkt].d[HBE_CIDB] = 0;
|
||
tq_pkt[pkt].d[HBE_CIDC] = 0;
|
||
tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) |
|
||
(drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD);
|
||
tq_pkt[pkt].d[HBE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */
|
||
tq_pkt[pkt].d[HBE_RSV] = 0;
|
||
PUTP32 (pkt, HBE_BADL, ba); /* bad addr */
|
||
tq_putr (pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Port last failure error log packet */
|
||
|
||
t_bool tq_plf (uint32 err)
|
||
{
|
||
int32 pkt;
|
||
|
||
if (!tq_deqf (&pkt)) return ERR; /* get log pkt */
|
||
tq_pkt[pkt].d[ELP_REFL] = tq_pkt[pkt].d[ELP_REFH] = 0; /* ref = 0 */
|
||
tq_pkt[pkt].d[ELP_UN] = tq_pkt[pkt].d[ELP_SEQ] = 0; /* no unit, seq */
|
||
tq_pkt[pkt].d[PLF_CIDA] = 0; /* cntl ID */
|
||
tq_pkt[pkt].d[PLF_CIDB] = 0;
|
||
tq_pkt[pkt].d[PLF_CIDC] = 0;
|
||
tq_pkt[pkt].d[PLF_CIDD] = (TQ_CLASS << PLF_CIDD_V_CLS) |
|
||
(drv_tab[tq_typ].cmod << PLF_CIDD_V_MOD);
|
||
tq_pkt[pkt].d[PLF_VER] = drv_tab[tq_typ].cver;
|
||
tq_pkt[pkt].d[PLF_ERR] = err;
|
||
tq_putr (pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT);
|
||
tq_pkt[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID);
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* Unit now available attention packet */
|
||
|
||
int32 tq_una (UNIT *uptr)
|
||
{
|
||
int32 pkt;
|
||
uint32 lu;
|
||
|
||
if (!tq_deqf (&pkt)) return ERR; /* get log pkt */
|
||
lu = uptr - tq_dev.units; /* get unit */
|
||
tq_pkt[pkt].d[RSP_REFL] = tq_pkt[pkt].d[RSP_REFH] = 0; /* ref = 0 */
|
||
tq_pkt[pkt].d[RSP_UN] = lu;
|
||
tq_pkt[pkt].d[RSP_RSV] = 0;
|
||
tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */
|
||
tq_putr (pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ); /* fill std fields */
|
||
return tq_putpkt (pkt, TRUE);
|
||
}
|
||
|
||
/* List handling
|
||
|
||
tq_deqf - dequeue head of free list (fatal err if none)
|
||
tq_deqh - dequeue head of list
|
||
tq_enqh - enqueue at head of list
|
||
tq_enqt - enqueue at tail of list
|
||
*/
|
||
|
||
t_bool tq_deqf (int32 *pkt)
|
||
{
|
||
if (tq_freq == 0) return tq_fatal (PE_NSR); /* no free pkts?? */
|
||
tq_pbsy = tq_pbsy + 1; /* cnt busy pkts */
|
||
*pkt = tq_freq; /* head of list */
|
||
tq_freq = tq_pkt[tq_freq].link; /* next */
|
||
return OK;
|
||
}
|
||
|
||
int32 tq_deqh (int32 *lh)
|
||
{
|
||
int32 ptr = *lh; /* head of list */
|
||
|
||
if (ptr) *lh = tq_pkt[ptr].link; /* next */
|
||
return ptr;
|
||
}
|
||
|
||
void tq_enqh (int32 *lh, int32 pkt)
|
||
{
|
||
if (pkt == 0) return; /* any pkt? */
|
||
tq_pkt[pkt].link = *lh; /* link is old lh */
|
||
*lh = pkt; /* pkt is new lh */
|
||
return;
|
||
}
|
||
|
||
void tq_enqt (int32 *lh, int32 pkt)
|
||
{
|
||
if (pkt == 0) return; /* any pkt? */
|
||
tq_pkt[pkt].link = 0; /* it will be tail */
|
||
if (*lh == 0) *lh = pkt; /* if empty, enqh */
|
||
else { uint32 ptr = *lh; /* chase to end */
|
||
while (tq_pkt[ptr].link) ptr = tq_pkt[ptr].link;
|
||
tq_pkt[ptr].link = pkt; } /* enq at tail */
|
||
return;
|
||
}
|
||
|
||
/* Packet and descriptor handling */
|
||
|
||
/* Get packet from command ring */
|
||
|
||
t_bool tq_getpkt (int32 *pkt)
|
||
{
|
||
uint32 desc;
|
||
t_addr addr;
|
||
|
||
if (!tq_getdesc (&tq_cq, &desc)) return ERR; /* get cmd desc */
|
||
if ((desc & UQ_DESC_OWN) == 0) { /* none */
|
||
*pkt = 0; /* pkt = 0 */
|
||
return OK; } /* no error */
|
||
if (!tq_deqf (pkt)) return ERR; /* get cmd pkt */
|
||
tq_hat = 0; /* dsbl hst timer */
|
||
addr = desc & UQ_ADDR; /* get Q22 addr */
|
||
if (Map_ReadW (addr + UQ_HDR_OFF, TQ_PKT_SIZE, tq_pkt[*pkt].d, MAP))
|
||
return tq_fatal (PE_PRE); /* read pkt */
|
||
return tq_putdesc (&tq_cq, desc); /* release desc */
|
||
}
|
||
|
||
/* Put packet to response ring - note the clever hack about credits.
|
||
The controller sends all its credits to the host. Thereafter, it
|
||
supplies one credit for every response packet sent over. Simple!
|
||
*/
|
||
|
||
t_bool tq_putpkt (int32 pkt, t_bool qt)
|
||
{
|
||
uint32 desc, lnt, cr;
|
||
t_addr addr;
|
||
|
||
if (pkt == 0) return OK; /* any packet? */
|
||
if (DBG_LOG (LOG_TQ)) {
|
||
UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]);
|
||
fprintf (sim_log, ">>TQ: rsp=%04X, sts=%04X",
|
||
tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS]);
|
||
if (up) fprintf (sim_log, ", pos=%d, obj=%d\n", up->pos, up->objp);
|
||
else fprintf (sim_log, "\n"); }
|
||
if (!tq_getdesc (&tq_rq, &desc)) return ERR; /* get rsp desc */
|
||
if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */
|
||
if (qt) tq_enqt (&tq_rspq, pkt); /* normal? q tail */
|
||
else tq_enqh (&tq_rspq, pkt); /* resp q call */
|
||
sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* activate q thrd */
|
||
return OK; }
|
||
addr = desc & UQ_ADDR; /* get Q22 addr */
|
||
lnt = tq_pkt[pkt].d[UQ_HLNT] - UQ_HDR_OFF; /* size, with hdr */
|
||
if ((GETP (pkt, UQ_HCTC, TYP) == UQ_TYP_SEQ) && /* seq packet? */
|
||
(GETP (pkt, CMD_OPC, OPC) & OP_END)) { /* end packet? */
|
||
cr = (tq_credits >= 14)? 14: tq_credits; /* max 14 credits */
|
||
tq_credits = tq_credits - cr; /* decr credits */
|
||
tq_pkt[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR); }
|
||
if (Map_WriteW (addr + UQ_HDR_OFF, lnt, tq_pkt[pkt].d, MAP))
|
||
return tq_fatal (PE_PWE); /* write pkt */
|
||
tq_enqh (&tq_freq, pkt); /* pkt is free */
|
||
tq_pbsy = tq_pbsy - 1; /* decr busy cnt */
|
||
if (tq_pbsy == 0) tq_hat = tq_htmo; /* idle? strt hst tmr */
|
||
return tq_putdesc (&tq_rq, desc); /* release desc */
|
||
}
|
||
|
||
/* Get a descriptor from the host */
|
||
|
||
t_bool tq_getdesc (struct uq_ring *ring, uint32 *desc)
|
||
{
|
||
t_addr addr = ring->ba + ring->idx;
|
||
uint16 d[2];
|
||
|
||
if (Map_ReadW (addr, 4, d, MAP)) /* fetch desc */
|
||
return tq_fatal (PE_QRE); /* err? dead */
|
||
*desc = ((uint32) d[0]) | (((uint32) d[1]) << 16);
|
||
return OK;
|
||
}
|
||
|
||
/* Return a descriptor to the host, clearing owner bit
|
||
If rings transitions from "empty" to "not empty" or "full" to
|
||
"not full", and interrupt bit was set, interrupt the host.
|
||
Actually, test whether previous ring entry was owned by host.
|
||
*/
|
||
|
||
t_bool tq_putdesc (struct uq_ring *ring, uint32 desc)
|
||
{
|
||
uint32 prvd, newd = (desc & ~UQ_DESC_OWN) | UQ_DESC_F;
|
||
t_addr 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 tq_fatal (PE_QWE); /* err? dead */
|
||
if (desc & UQ_DESC_F) { /* was F set? */
|
||
if (ring->lnt <= 4) tq_ring_int (ring); /* lnt = 1? intr */
|
||
else { prva = ring->ba + /* prv desc */
|
||
((ring->idx - 4) & (ring->lnt - 1));
|
||
if (Map_ReadW (prva, 4, d, MAP)) /* read prv */
|
||
return tq_fatal (PE_QRE);
|
||
prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16);
|
||
if (prvd & UQ_DESC_OWN) tq_ring_int (ring); } }
|
||
ring->idx = (ring->idx + 4) & (ring->lnt - 1);
|
||
return OK;
|
||
}
|
||
|
||
/* Get unit descriptor for logical unit - trivial now,
|
||
but eventually, hide multiboard complexities here */
|
||
|
||
UNIT *tq_getucb (uint32 lu)
|
||
{
|
||
UNIT *uptr;
|
||
|
||
if (lu >= TQ_NUMDR) return NULL;
|
||
uptr = tq_dev.units + lu;
|
||
if (uptr->flags & UNIT_DIS) return NULL;
|
||
return uptr;
|
||
}
|
||
|
||
/* Hack unit flags */
|
||
|
||
void tq_setf_unit (int32 pkt, UNIT *uptr)
|
||
{
|
||
uptr->uf = tq_pkt[pkt].d[ONL_UFL] & UF_MSK; /* settable flags */
|
||
if ((tq_pkt[pkt].d[CMD_MOD] & MD_SWP) && /* swre wrp enb? */
|
||
(tq_pkt[pkt].d[ONL_UFL] & UF_WPS)) /* swre wrp on? */
|
||
uptr->uf = uptr->uf | UF_WPS; /* simon says... */
|
||
return;
|
||
}
|
||
|
||
/* Hack end flags */
|
||
|
||
uint32 tq_efl (UNIT *uptr)
|
||
{
|
||
uint32 t = 0;
|
||
|
||
if (uptr) { /* any unit? */
|
||
if (uptr->flags & UNIT_POL) t = t | EF_PLS; /* note pos lost */
|
||
if (uptr->flags & UNIT_SXC) t = t | EF_SXC; /* note ser exc */
|
||
if (TEST_EOT (uptr)) t = t | EF_EOT; } /* note EOT */
|
||
return t;
|
||
}
|
||
|
||
/* Unit response fields */
|
||
|
||
void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all)
|
||
{
|
||
tq_pkt[pkt].d[ONL_MLUN] = lu; /* multi-unit */
|
||
tq_pkt[pkt].d[ONL_UFL] = uptr->uf | TQ_WPH (uptr); /* unit flags */
|
||
tq_pkt[pkt].d[ONL_RSVL] = tq_pkt[pkt].d[ONL_RSVH] = 0; /* reserved */
|
||
tq_pkt[pkt].d[ONL_UIDA] = lu; /* UID low */
|
||
tq_pkt[pkt].d[ONL_UIDB] = 0;
|
||
tq_pkt[pkt].d[ONL_UIDC] = 0;
|
||
tq_pkt[pkt].d[ONL_UIDD] = (UID_TAPE << ONL_UIDD_V_CLS) |
|
||
(drv_tab[tq_typ].umod << ONL_UIDD_V_MOD); /* UID hi */
|
||
PUTP32 (pkt, ONL_MEDL, drv_tab[tq_typ].med); /* media type */
|
||
if (all) { /* if long form */
|
||
tq_pkt[pkt].d[ONL_FMT] = drv_tab[tq_typ].fmt; /* format */
|
||
tq_pkt[pkt].d[ONL_SPD] = 0; /* speed */
|
||
PUTP32 (pkt, ONL_MAXL, TQ_MAXFR); /* max xfr */
|
||
tq_pkt[pkt].d[ONL_NREC] = 0; /* noise rec */
|
||
tq_pkt[pkt].d[ONL_RSVE] = 0; } /* reserved */
|
||
return;
|
||
}
|
||
|
||
/* UQ_HDR and RSP_OP fields */
|
||
|
||
void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ)
|
||
{
|
||
tq_pkt[pkt].d[RSP_OPF] = (cmd << RSP_OPF_V_OPC) | /* set cmd, flg */
|
||
(flg << RSP_OPF_V_FLG);
|
||
tq_pkt[pkt].d[RSP_STS] = sts;
|
||
tq_pkt[pkt].d[UQ_HLNT] = lnt; /* length */
|
||
tq_pkt[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) | /* type, cid */
|
||
(UQ_CID_TMSCP << UQ_HCTC_V_CID); /* clr credits */
|
||
return;
|
||
}
|
||
|
||
/* Post interrupt during init */
|
||
|
||
void tq_init_int (void)
|
||
{
|
||
if ((tq_s1dat & SA_S1H_IE) && tq_dib.vec) SET_INT (TQ);
|
||
return;
|
||
}
|
||
|
||
/* Post interrupt during putpkt - note that NXMs are ignored! */
|
||
|
||
void tq_ring_int (struct uq_ring *ring)
|
||
{
|
||
t_addr iadr = tq_comm + ring->ioff; /* addr intr wd */
|
||
uint16 flag = 1;
|
||
|
||
Map_WriteW (iadr, 2, &flag, MAP); /* write flag */
|
||
if (tq_dib.vec) SET_INT (TQ); /* if enb, intr */
|
||
return;
|
||
}
|
||
|
||
/* Return interrupt vector */
|
||
|
||
int32 tq_inta (void)
|
||
{
|
||
return tq_dib.vec; /* prog vector */
|
||
}
|
||
|
||
/* Fatal error */
|
||
|
||
t_bool tq_fatal (uint32 err)
|
||
{
|
||
if (DBG_LOG (LOG_TQ)) fprintf (sim_log, ">>TQ: fatal err=%X\n", err);
|
||
tq_reset (&tq_dev); /* reset device */
|
||
tq_sa = SA_ER | err; /* SA = dead code */
|
||
tq_csta = CST_DEAD; /* state = dead */
|
||
tq_perr = err; /* save error */
|
||
return ERR;
|
||
}
|
||
|
||
/* Change controller type */
|
||
|
||
t_stat tq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||
{
|
||
tq_typ = val;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Device attach */
|
||
|
||
t_stat tq_attach (UNIT *uptr, char *cptr)
|
||
{
|
||
t_stat r;
|
||
|
||
r = attach_unit (uptr, cptr);
|
||
if (r != SCPE_OK) return r;
|
||
if (tq_csta == CST_UP) uptr->flags = (uptr->flags | UNIT_ATP) &
|
||
~(UNIT_SXC | UNIT_POL | UNIT_TMK);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Device detach */
|
||
|
||
t_stat tq_detach (UNIT *uptr)
|
||
{
|
||
t_stat r;
|
||
|
||
r = detach_unit (uptr); /* detach unit */
|
||
if (r != SCPE_OK) return r;
|
||
uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP | UNIT_SXC | UNIT_POL | UNIT_TMK);
|
||
uptr->uf = 0; /* clr unit flgs */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Device reset */
|
||
|
||
t_stat tq_reset (DEVICE *dptr)
|
||
{
|
||
int32 i, j;
|
||
UNIT *uptr;
|
||
|
||
tq_csta = CST_S1; /* init stage 1 */
|
||
tq_s1dat = 0; /* no S1 data */
|
||
tq_dib.vec = 0; /* no vector */
|
||
tq_sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP; /* init SA val */
|
||
tq_cflgs = CF_RPL; /* ctrl flgs off */
|
||
tq_htmo = TQ_DHTMO; /* default timeout */
|
||
tq_hat = tq_htmo; /* default timer */
|
||
tq_cq.ba = tq_cq.lnt = tq_cq.idx = 0; /* clr cmd ring */
|
||
tq_rq.ba = tq_rq.lnt = tq_rq.idx = 0; /* clr rsp ring */
|
||
tq_credits = (TQ_NPKTS / 2) - 1; /* init credits */
|
||
tq_freq = 1; /* init free list */
|
||
for (i = 0; i < TQ_NPKTS; i++) { /* all pkts free */
|
||
if (i) tq_pkt[i].link = (i + 1) & TQ_M_NPKTS;
|
||
else tq_pkt[i].link = 0;
|
||
for (j = 0; j < TQ_PKT_SIZE_W; j++) tq_pkt[i].d[j] = 0; }
|
||
tq_rspq = 0; /* no q'd rsp pkts */
|
||
tq_pbsy = 0; /* all pkts free */
|
||
tq_pip = 0; /* not polling */
|
||
CLR_INT (TQ); /* clr intr req */
|
||
for (i = 0; i < TQ_NUMDR + 2; i++) { /* init units */
|
||
uptr = tq_dev.units + i;
|
||
sim_cancel (uptr); /* clr activity */
|
||
uptr->flags = uptr->flags & /* not online */
|
||
~(UNIT_ONL|UNIT_ATP|UNIT_SXC|UNIT_POL|UNIT_TMK);
|
||
uptr->uf = 0; /* clr unit flags */
|
||
uptr->cpkt = uptr->pktq = 0; } /* clr pkt q's */
|
||
if (tqxb == NULL) tqxb = calloc (TQ_MAXFR, sizeof (uint8));
|
||
if (tqxb == NULL) return SCPE_MEM;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Device bootstrap */
|
||
|
||
#if defined (VM_PDP11)
|
||
|
||
#define BOOT_START 016000 /* start */
|
||
#define BOOT_ENTRY (BOOT_START + 002) /* entry */
|
||
#define BOOT_UNIT (BOOT_START + 010) /* unit number */
|
||
#define BOOT_CSR (BOOT_START + 014) /* CSR */
|
||
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
|
||
|
||
|
||
/* Data structure definitions */
|
||
|
||
#define B_CMDINT (BOOT_START - 01000) /* cmd int */
|
||
#define B_RSPINT (B_CMDINT + 002) /* rsp int */
|
||
#define B_RING (B_RSPINT + 002) /* ring base */
|
||
#define B_RSPH (B_RING + 010) /* resp pkt hdr */
|
||
#define B_TKRSP (B_RSPH + 004) /* resp pkt */
|
||
#define B_CMDH (B_TKRSP + 060) /* cmd pkt hdr */
|
||
#define B_TKCMD (B_CMDH + 004) /* cmd pkt */
|
||
#define B_UNIT (B_TKCMD + 004) /* unit # */
|
||
|
||
static const uint16 boot_rom[] = {
|
||
|
||
0046525, /* ST: "UM" */
|
||
|
||
0012706, 0016000, /* mov #st,sp */
|
||
0012700, 0000000, /* mov #unitno,r0 */
|
||
0012701, 0174500, /* mov #174500,r1 ; ip addr */
|
||
0005021, /* clr (r1)+ ; init */
|
||
0012704, 0004000, /* mov #4000,r4 ; s1 mask */
|
||
0005002, /* clr r2 */
|
||
0005022, /* 10$: clr (r2)+ ; clr up to boot */
|
||
0020237, BOOT_START - 2, /* cmp r2,#st-2 */
|
||
0103774, /* blo 10$ */
|
||
0012705, BOOT_START+0312, /* mov #cmdtbl,r5 ; addr of tbl */
|
||
|
||
/* Four step init process */
|
||
|
||
0005711, /* 20$: tst (r1) ; err? */
|
||
0100001, /* bpl 30$ */
|
||
0000000, /* halt */
|
||
0030411, /* 30$: bit r4,(r1) ; step set? */
|
||
0001773, /* beq 20$ ; wait */
|
||
0012511, /* mov (r5)+,(r1) ; send next */
|
||
0006304, /* asl r4 ; next mask */
|
||
0100370, /* bpl 20$ ; s4 done? */
|
||
|
||
/* Set up rings, issue ONLINE, REWIND, READ */
|
||
|
||
0012737, 0000400, B_CMDH + 2, /* mov #400,cmdh+2 ; VCID = 1 */
|
||
0012737, 0000044, B_CMDH, /* mov #36.,cmdh ; cmd pkt lnt */
|
||
0010037, B_UNIT, /* mov r0,unit ; unit # */
|
||
0012737, 0000011, B_TKCMD + 8, /* mov #11,tkcmd+8. ; online op */
|
||
0012737, 0020000, B_TKCMD + 10, /* mov #20000,tkcmd+10. ; clr ser ex */
|
||
0012702, B_RING, /* mov #ring,r2 ; init rings */
|
||
0012722, B_TKRSP, /* mov #tkrsp,(r2)+ ; rsp pkt addr */
|
||
0010203, /* mov r2,r3 ; save ring+2 */
|
||
0010423, /* mov r4,(r3)+ ; set TK own */
|
||
0012723, B_TKCMD, /* mov #tkcmd,(r3)+ ; cmd pkt addr */
|
||
0010423, /* mov r4,(r3)+ ; set TK own */
|
||
0005741, /* tst -(r1) ; start poll */
|
||
0005712, /* 40$: tst (r2) ; wait for resp */
|
||
0100776, /* bmi 40$ */
|
||
0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */
|
||
0001401, /* beq 50$ */
|
||
0000000, /* halt */
|
||
0012703, B_TKCMD + 8, /* 50$: mov #tkcmd+8.,r3 */
|
||
0012723, 0000045, /* mov #45,(r3)+ ; reposition */
|
||
0012723, 0020002, /* mov #20002,(r3)+ ; rew, clr exc */
|
||
0012723, 0000001, /* mov #1,(r3)+ ; lo rec skp */
|
||
0005023, /* clr (r3)+ ; hi rec skp */
|
||
0005023, /* clr (r3)+ ; lo tmk skp */
|
||
0005023, /* clr (r3)+ ; hi tmk skp */
|
||
0010412, /* mov r4,(r2) ; TK own rsp */
|
||
0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */
|
||
0005711, /* tst (r1) ; start poll */
|
||
0005712, /* 60$: tst (r2) ; wait for resp */
|
||
0100776, /* bmi 60$ */
|
||
0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */
|
||
0001401, /* beq 70$ */
|
||
0000000, /* halt */
|
||
0012703, B_TKCMD + 8, /* 70$: mov #tkcmd+8.,r3 */
|
||
0012723, 0000041, /* mov #41,(r3)+ ; read */
|
||
0012723, 0020000, /* mov #20000,(r3)+ ; clr exc */
|
||
0012723, 0001000, /* mov #512.,(r3)+ ; bc = 512 */
|
||
0005023, /* clr (r3)+ ; clr args */
|
||
0005023, /* clr (r3)+ ; ba = 0 */
|
||
0010412, /* mov r4,(r2) ; TK own rsp */
|
||
0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */
|
||
0005711, /* tst (r1) ; start poll */
|
||
0005712, /* 80$: tst (r2) ; wait for resp */
|
||
0100776, /* bmi 80$ */
|
||
0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */
|
||
0001401, /* beq 90$ */
|
||
0000000, /* halt */
|
||
|
||
/* Boot block read in, jump to 0 - leave controller init'd */
|
||
|
||
0005003, /* clr r3 */
|
||
0012704, BOOT_START+020, /* mov #st+020,r4 */
|
||
0005005, /* clr r5 */
|
||
0005007, /* clr pc */
|
||
|
||
0100000, /* cmdtbl: init step 1 */
|
||
B_RING, /* ring base */
|
||
0000000, /* high ring base */
|
||
0000001 /* go */
|
||
};
|
||
|
||
t_stat tq_boot (int32 unitno, DEVICE *dptr)
|
||
{
|
||
int32 i;
|
||
extern int32 saved_PC;
|
||
extern uint16 *M;
|
||
|
||
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] = tq_dib.ba & DMASK;
|
||
saved_PC = BOOT_ENTRY;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
#else
|
||
|
||
t_stat tq_boot (int32 unitno, DEVICE *dptr)
|
||
{
|
||
return SCPE_NOFNC;
|
||
}
|
||
#endif
|
||
|
||
/* Special show commands */
|
||
|
||
void tq_show_ring (FILE *st, struct uq_ring *rp)
|
||
{
|
||
uint32 i, desc;
|
||
uint16 d[2];
|
||
|
||
#if defined (VM_PDP11)
|
||
fprintf (st, "ring, base = %o, index = %d, length = %d\n",
|
||
rp->ba, rp->idx >> 2, rp->lnt >> 2);
|
||
#else
|
||
fprintf (st, "ring, base = %x, index = %d, length = %d\n",
|
||
rp->ba, rp->idx >> 2, rp->lnt >> 2);
|
||
#endif
|
||
for (i = 0; i < (rp->lnt >> 2); i++) {
|
||
if (Map_ReadW (rp->ba + (i << 2), 4, d, 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 tq_show_pkt (FILE *st, int32 pkt)
|
||
{
|
||
int32 i, j;
|
||
uint32 cr = GETP (pkt, UQ_HCTC, CR);
|
||
uint32 typ = GETP (pkt, UQ_HCTC, TYP);
|
||
uint32 cid = GETP (pkt, UQ_HCTC, CID);
|
||
|
||
fprintf (st, "packet %d, credits = %d, type = %d, cid = %d\n",
|
||
pkt, cr, typ, cid);
|
||
for (i = 0; i < TQ_SH_MAX; i = i + TQ_SH_PPL) {
|
||
fprintf (st, " %2d:", i);
|
||
for (j = i; j < (i + TQ_SH_PPL); j++)
|
||
#if defined (VM_PDP11)
|
||
fprintf (st, " %06o", tq_pkt[pkt].d[j]);
|
||
#else
|
||
fprintf (st, " %04x", tq_pkt[pkt].d[j]);
|
||
#endif
|
||
fprintf (st, "\n");
|
||
}
|
||
return;
|
||
}
|
||
|
||
t_stat tq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||
{
|
||
int32 pkt, u = uptr - tq_dev.units;
|
||
|
||
if (tq_csta != CST_UP) {
|
||
fprintf (st, "Controller is not initialized\n");
|
||
return SCPE_OK; }
|
||
if ((uptr->flags & UNIT_ONL) == 0) {
|
||
if (uptr->flags & UNIT_ATT)
|
||
fprintf (st, "Unit %d is available\n", u);
|
||
else fprintf (st, "Unit %d is offline\n", u);
|
||
return SCPE_OK; }
|
||
if (uptr->cpkt) {
|
||
fprintf (st, "Unit %d current ", u);
|
||
tq_show_pkt (st, uptr->cpkt);
|
||
if (pkt = uptr->pktq) {
|
||
do { fprintf (st, "Unit %d queued ", u);
|
||
tq_show_pkt (st, pkt); }
|
||
while (pkt = tq_pkt[pkt].link); } }
|
||
else fprintf (st, "Unit %d queues are empty\n", u);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat tq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||
{
|
||
int32 i, pkt;
|
||
|
||
if (tq_csta != CST_UP) {
|
||
fprintf (st, "Controller is not initialized\n");
|
||
return SCPE_OK; }
|
||
if (val & TQ_SH_RI) {
|
||
if (tq_pip) fprintf (st, "Polling in progress, host timer = %d\n", tq_hat);
|
||
else fprintf (st, "Host timer = %d\n", tq_hat);
|
||
fprintf (st, "Command ");
|
||
tq_show_ring (st, &tq_cq);
|
||
fprintf (st, "Response ");
|
||
tq_show_ring (st, &tq_rq);
|
||
}
|
||
if (val & TQ_SH_FR) {
|
||
if (pkt = tq_freq) {
|
||
for (i = 0; pkt != 0; i++, pkt = tq_pkt[pkt].link) {
|
||
if (i == 0) fprintf (st, "Free queue = %d", pkt);
|
||
else if ((i % 16) == 0) fprintf (st, ",\n %d", pkt);
|
||
else fprintf (st, ", %d", pkt); }
|
||
fprintf (st, "\n"); }
|
||
else fprintf (st, "Free queue is empty\n");
|
||
}
|
||
if (val & TQ_SH_RS) {
|
||
if (pkt = tq_rspq) {
|
||
do { fprintf (st, "Response ");
|
||
tq_show_pkt (st, pkt); }
|
||
while (pkt = tq_pkt[pkt].link); }
|
||
else fprintf (st, "Response queue is empty\n");
|
||
}
|
||
if (val & TQ_SH_UN) {
|
||
for (i = 0; i < TQ_NUMDR; i++)
|
||
tq_show_unitq (st, &tq_unit[i], 0, NULL);
|
||
}
|
||
return SCPE_OK;
|
||
}
|