RESTRICTION: The HP DS disk is not debugged. DO NOT enable this feature for normal operations. WARNING: Massive changes in the PDP-11 make all previous SAVEd file obsolete. Do not attempt to use a PDP-11 SAVE file from a prior release with V3.3! 1. New Features in 3.3 1.1 SCP - Added -p (powerup) qualifier to RESET - Changed SET <unit> ONLINE/OFFLINE to SET <unit> ENABLED/DISABLED - Moved SET DEBUG under SET CONSOLE hierarchy - Added optional parameter value to SHOW command - Added output file option to SHOW command 1.2 PDP-11 - Separated RH Massbus adapter from RP controller - Added TU tape support - Added model emulation framework - Added model details 1.3 VAX - Separated out CVAX-specific features from core instruction simulator - Implemented capability for CIS, octaword, compatibility mode instructions - Added instruction display and parse for compatibility mode - Changed SET CPU VIRTUAL=n to SHOW CPU VIRTUAL=n - Added =n optional parameter to SHOW CPU HISTORY 1.4 Unibus/Qbus simulators (PDP-11, VAX, PDP-10) - Simplified DMA API's - Modified DMA peripherals to use simplified API's 1.5 HP2100 (all changes from Dave Bryan) CPU - moved MP into its own device; added MP option jumpers - modified DMA to allow disabling - modified SET CPU 2100/2116 to truncate memory > 32K - added -F switch to SET CPU to force memory truncation - modified WRU to be REG_HRO - added BRK and DEL to save console settings DR - provided protected tracks and "Writing Enabled" status bit - added "parity error" status return on writes for 12606 - added track origin test for 12606 - added SCP test for 12606 - added "Sector Flag" status bit - added "Read Inhibit" status bit for 12606 - added TRACKPROT modifier LPS - added SET OFFLINE/ONLINE, POWEROFF/POWERON - added fast/realistic timing - added debug printouts LPT - added SET OFFLINE/ONLINE, POWEROFF/POWERON PTR - added paper tape loop mode, DIAG/READER modifiers to PTR - added PV_LEFT to PTR TRLLIM register CLK - modified CLK to permit disable 1.6 IBM 1401, IBM 1620, Interdata 16b, SDS 940, PDP-10 - Added instruction history 1.7 H316, PDP-15, PDP-8 - Added =n optional value to SHOW CPU HISTORY 2. Bugs Fixed in 3.3 2.1 SCP - Fixed comma-separated SET options (from Dave Bryan) - Fixed duplicate HELP displays with user-specified commands 2.2 PDP-10 - Replicated RP register state per drive - Fixed TU to set FCE on short record - Fixed TU to return bit<15> in drive type - Fixed TU format specification, 1:0 are don't cares - Fixed TU handling of TMK status - Fixed TU handling of DONE, ATA at end of operation - Implemented TU write check 2.3 PDP-11 - Replicated RP register state per drive - Fixed RQ, TQ to report correct controller type and stage 1 configuration flags on a Unibus system - Fixed HK CS2<output_ready> flag 2.4 VAX - Fixed parsing of indirect displacement modes in instruction input 2.5 HP2100 (all fixes from Dave Bryan) CPU - fixed S-register behavior on 2116 - fixed LIx/MIx behavior for DMA on 2116 and 2100 - fixed LIx/MIx behavior for empty I/O card slots DP - fixed enable/disable from either device - fixed ANY ERROR status for 12557A interface - fixed unattached drive status for 12557A interface - status cmd without prior STC DC now completes (12557A) - OTA/OTB CC on 13210A interface also does CLC CC - fixed RAR model - fixed seek check on 13210 if sector out of range DQ - fixed enable/disable from either device - shortened xtime from 5 to 3 (drive avg 156KW/second) - fixed not ready/any error status - fixed RAR model DR - fixed enable/disable from either device - fixed sector return in status word - fixed DMA last word write, incomplete sector fill value - fixed 12610 SFC operation - fixed current-sector determination IPL - fixed enable/disable from either device LPS - fixed status returns for error conditions - fixed handling of non-printing characters - fixed handling of characters after column 80 - improved timing model accuracy for RTE LPT - fixed status returns for error conditions - fixed TOF handling so form remains on line 0 SYS - fixed display of CCA/CCB/CCE instructions 2.5 PDP-15 FPP - fixed URFST to mask low 9b of fraction - fixed exception PC setting
1364 lines
37 KiB
C
1364 lines
37 KiB
C
/* pdp11_vh.c: DHQ11 asynchronous terminal multiplexor simulator
|
||
|
||
Copyright (c) 2004, John A. Dundas III
|
||
Portions derived from work by 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
|
||
THE AUTHOR 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 the Author shall not
|
||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||
in this Software without prior written authorization from the Author.
|
||
|
||
vh DHQ11 asynch multiplexor for SIMH
|
||
|
||
12-Jun-04 RMS Repair MS2SIMH macro to avoid possible divide by
|
||
0 bug.
|
||
8-Jun-04 JAD Repair vh_dev initialization; remove unused
|
||
variables, cast to avoid conversion confusion.
|
||
7-Jun-04 JAD Complete function prototypes of forward declarations.
|
||
Repair broken prototypes of vh_rd() and vh_wr().
|
||
Explicitly size integer declarations.
|
||
4-Jun-04 JAD Preliminary code: If operating in a PDP-11 Unibus
|
||
environment, force DHU mode.
|
||
29-May-04 JAD Make certain RX.TIMER is within allowable range.
|
||
25-May-04 JAD All time-based operations are scaled by tmxr_poll
|
||
units.
|
||
23-May-04 JAD Change to fifo_get() and dq_tx_report() to avoid
|
||
gratuitous stack manipulation.
|
||
20-May-04 JAD Made modem control and auto-hangup unit flags
|
||
19-May-04 JAD Fix problem with modem status where the line number
|
||
was not being included
|
||
12-May-04 JAD Revised for updated tmxr interfaces
|
||
28-Jan-04 JAD Original creation and testing
|
||
|
||
I/O Page Registers
|
||
|
||
CSR 17 760 440 (float)
|
||
|
||
Vector: 300 (float)
|
||
|
||
Priority: BR4
|
||
|
||
Rank: 32
|
||
|
||
*/
|
||
/* MANY constants needed! */
|
||
|
||
#if defined (VM_VAX)
|
||
#include "vax_defs.h"
|
||
extern int32 int_req[IPL_HLVL];
|
||
extern int32 int_vec[IPL_HLVL][32];
|
||
#endif
|
||
|
||
#if defined (VM_PDP11)
|
||
#include "pdp11_defs.h"
|
||
extern int32 int_req[IPL_HLVL];
|
||
extern int32 int_vec[IPL_HLVL][32];
|
||
extern int32 cpu_opt;
|
||
#endif
|
||
|
||
#include "sim_sock.h"
|
||
#include "sim_tmxr.h"
|
||
|
||
/* imports from pdp11_stddev.c: */
|
||
extern int32 tmxr_poll, clk_tps;
|
||
/* convert ms to SIMH time units based on tmxr_poll polls per second */
|
||
#define MS2SIMH(ms) (((ms) * clk_tps) / 1000)
|
||
extern FILE *sim_log;
|
||
|
||
#ifndef VH_MUXES
|
||
#define VH_MUXES (4)
|
||
#endif
|
||
#define VH_MNOMASK (VH_MUXES - 1)
|
||
|
||
#define VH_LINES (8)
|
||
|
||
#define UNIT_V_MODEDHU (UNIT_V_UF + 0)
|
||
#define UNIT_V_FASTDMA (UNIT_V_UF + 1)
|
||
#define UNIT_V_MODEM (UNIT_V_UF + 2)
|
||
#define UNIT_V_HANGUP (UNIT_V_UF + 3)
|
||
#define UNIT_MODEDHU (1 << UNIT_V_MODEDHU)
|
||
#define UNIT_FASTDMA (1 << UNIT_V_FASTDMA)
|
||
#define UNIT_MODEM (1 << UNIT_V_MODEM)
|
||
#define UNIT_HANGUP (1 << UNIT_V_HANGUP)
|
||
|
||
/* VHCSR - 160440 - Control and Status Register */
|
||
|
||
#define CSR_M_IND_ADDR (017)
|
||
#define CSR_SKIP (1 << 4)
|
||
#define CSR_MASTER_RESET (1 << 5)
|
||
#define CSR_RXIE (1 << 6)
|
||
#define CSR_RX_DATA_AVAIL (1 << 7)
|
||
#define CSR_M_TX_LINE (017)
|
||
#define CSR_V_TX_LINE (8)
|
||
#define CSR_TX_DMA_ERR (1 << 12)
|
||
#define CSR_DIAG_FAIL (1 << 13)
|
||
#define CSR_TXIE (1 << 14)
|
||
#define CSR_TX_ACTION (1 << 15)
|
||
#define CSR_GETCHAN(x) ((x) & CSR_M_IND_ADDR)
|
||
#define CSR_RW \
|
||
(CSR_TXIE|CSR_RXIE|CSR_SKIP|CSR_M_IND_ADDR|CSR_MASTER_RESET)
|
||
#define RESET_ABORT (052525)
|
||
|
||
/* Receive Buffer (RBUF) */
|
||
|
||
#define FIFO_SIZE (256)
|
||
#define FIFO_ALARM (191)
|
||
#define FIFO_HALF (FIFO_SIZE / 2)
|
||
#define RBUF_M_RX_CHAR (0377)
|
||
#define RBUF_M_RX_LINE (07)
|
||
#define RBUF_V_RX_LINE (8)
|
||
#define RBUF_PARITY_ERR (1 << 12)
|
||
#define RBUF_FRAME_ERR (1 << 13)
|
||
#define RBUF_OVERRUN_ERR (1 << 14)
|
||
#define RBUF_DATA_VALID (1 << 15)
|
||
#define RBUF_GETLINE(x) (((x) >> RBUF_V_RX_LINE) & RBUF_M_RX_LINE)
|
||
#define RBUF_PUTLINE(x) ((x) << RBUF_V_RX_LINE)
|
||
#define RBUF_DIAG \
|
||
(RBUF_PARITY_ERR|RBUF_FRAME_ERR|RBUF_OVERRUN_ERR)
|
||
#define XON (021)
|
||
#define XOFF (023)
|
||
|
||
/* Transmit Character Register (TXCHAR) */
|
||
|
||
#define TXCHAR_M_CHAR (0377)
|
||
#define TXCHAR_TX_DATA_VALID (1 << 15)
|
||
|
||
/* Receive Timer Register (RXTIMER) */
|
||
|
||
#define RXTIMER_M_RX_TIMER (0377)
|
||
|
||
/* Line-Parameter Register (LPR) */
|
||
|
||
#define LPR_DISAB_XRPT (1 << 0) /* not impl. in real DHU */
|
||
#define LPR_V_DIAG (1)
|
||
#define LPR_M_DIAG (03)
|
||
#define LPR_V_CHAR_LGTH (3)
|
||
#define LPR_M_CHAR_LGTH (03)
|
||
#define LPR_PARITY_ENAB (1 << 5)
|
||
#define LPR_EVEN_PARITY (1 << 6)
|
||
#define LPR_STOP_CODE (1 << 7)
|
||
#define LPR_V_RX_SPEED (8)
|
||
#define LPR_M_RX_SPEED (017)
|
||
#define LPR_V_TX_SPEED (12)
|
||
#define LPR_M_TX_SPEED (017)
|
||
|
||
#define RATE_50 (0)
|
||
#define RATE_75 (1)
|
||
#define RATE_110 (2)
|
||
#define RATE_134 (3)
|
||
#define RATE_150 (4)
|
||
#define RATE_300 (5)
|
||
#define RATE_600 (6)
|
||
#define RATE_1200 (7)
|
||
#define RATE_1800 (8)
|
||
#define RATE_2000 (9)
|
||
#define RATE_2400 (10)
|
||
#define RATE_4800 (11)
|
||
#define RATE_7200 (12)
|
||
#define RATE_9600 (13)
|
||
#define RATE_19200 (14)
|
||
#define RATE_38400 (15)
|
||
|
||
/* Line-Status Register (STAT) */
|
||
|
||
#define STAT_DHUID (1 << 8) /* mode: 0=DHV, 1=DHU */
|
||
#define STAT_MDL (1 << 9) /* always 0, has modem support */
|
||
#define STAT_CTS (1 << 11) /* CTS from modem */
|
||
#define STAT_DCD (1 << 12) /* DCD from modem */
|
||
#define STAT_RI (1 << 13) /* RI from modem */
|
||
#define STAT_DSR (1 << 15) /* DSR from modem */
|
||
|
||
/* FIFO Size Register (FIFOSIZE) */
|
||
|
||
#define FIFOSIZE_M_SIZE (0377)
|
||
|
||
/* FIFO Data Register (FIFODATA) */
|
||
|
||
#define FIFODATA_W0 (0377)
|
||
#define FIFODATA_V_W1 (8)
|
||
#define FIFODATA_M_W1 (0377)
|
||
|
||
/* Line-Control Register (LNCTRL) */
|
||
|
||
#define LNCTRL_TX_ABORT (1 << 0)
|
||
#define LNCTRL_IAUTO (1 << 1)
|
||
#define LNCTRL_RX_ENA (1 << 2)
|
||
#define LNCTRL_BREAK (1 << 3)
|
||
#define LNCTRL_OAUTO (1 << 4)
|
||
#define LNCTRL_FORCE_XOFF (1 << 5)
|
||
#define LNCTRL_V_MAINT (6)
|
||
#define LNCTRL_M_MAINT (03)
|
||
#define LNCTRL_LINK_TYPE (1 << 8) /* 0=data leads only, 1=modem */
|
||
#define LNCTRL_DTR (1 << 9) /* DTR to modem */
|
||
#define LNCTRL_RTS (1 << 12) /* RTS to modem */
|
||
|
||
/* Transmit Buffer Address Register Number 1 (TBUFFAD1) */
|
||
|
||
/* Transmit Buffer Address Register Number 2 (TBUFFAD2) */
|
||
|
||
#define TB2_M_TBUFFAD (077)
|
||
#define TB2_TX_DMA_START (1 << 7)
|
||
#define TB2_TX_ENA (1 << 15)
|
||
|
||
/* Transmit DMA Buffer Counter (TBUFFCT) */
|
||
|
||
/* Self-Test Error Codes */
|
||
|
||
#define SELF_NULL (0201)
|
||
#define SELF_SKIP (0203)
|
||
#define SELF_OCT (0211)
|
||
#define SELF_RAM (0225)
|
||
#define SELF_RCD (0231)
|
||
#define SELF_DRD (0235)
|
||
|
||
#define BMP_OK (0305)
|
||
#define BMP_BAD (0307)
|
||
|
||
/* Loopback types */
|
||
|
||
#define LOOP_NONE (0)
|
||
#define LOOP_H325 (1)
|
||
#define LOOP_H3101 (2) /* p.2-13 DHQ manual */
|
||
/* Local storage */
|
||
|
||
static uint16 vh_csr[VH_MUXES] = { 0 }; /* CSRs */
|
||
static uint16 vh_timer[VH_MUXES] = { 1 }; /* controller timeout */
|
||
static uint16 vh_mcount[VH_MUXES] = { 0 };
|
||
static uint32 vh_timeo[VH_MUXES] = { 0 };
|
||
static uint32 vh_ovrrun[VH_MUXES] = { 0 }; /* line overrun bits */
|
||
/* XOFF'd channels, one bit/channel */
|
||
static uint32 vh_stall[VH_MUXES] = { 0 };
|
||
static uint16 vh_loop[VH_MUXES] = { 0 }; /* loopback status */
|
||
|
||
/* One bit per controller: */
|
||
static uint32 vh_rxi = 0; /* rcv interrupts */
|
||
static uint32 vh_txi = 0; /* xmt interrupts */
|
||
static uint32 vh_crit = 0; /* FIFO.CRIT */
|
||
|
||
static const int32 bitmask[4] = { 037, 077, 0177, 0377 };
|
||
|
||
/* RX FIFO state */
|
||
|
||
static int32 rbuf_idx[VH_MUXES] = { 0 };/* index into vh_rbuf */
|
||
static uint32 vh_rbuf[VH_MUXES][FIFO_SIZE] = { 0 };
|
||
|
||
/* TXQ state */
|
||
|
||
#define TXQ_SIZE (16)
|
||
static int32 txq_idx[VH_MUXES] = { 0 };
|
||
static uint32 vh_txq[VH_MUXES][TXQ_SIZE] = { 0 };
|
||
|
||
/* Need to extend the TMLN structure */
|
||
|
||
typedef struct {
|
||
TMLN *tmln;
|
||
uint16 lpr; /* line parameters */
|
||
uint16 lnctrl; /* line control */
|
||
uint16 lstat; /* line modem status */
|
||
uint16 tbuffct; /* remaining character count */
|
||
uint16 tbuf1;
|
||
uint16 tbuf2;
|
||
uint16 txchar; /* single character I/O */
|
||
} TMLX;
|
||
|
||
static TMLN vh_ldsc[VH_MUXES * VH_LINES] = { 0 };
|
||
static TMXR vh_desc = { VH_MUXES * VH_LINES, 0, 0, vh_ldsc };
|
||
static TMLX vh_parm[VH_MUXES * VH_LINES] = { 0 };
|
||
|
||
/* Forward references */
|
||
static t_stat vh_rd (int32 *data, int32 PA, int32 access);
|
||
static t_stat vh_wr (int32 data, int32 PA, int32 access);
|
||
static t_stat vh_svc (UNIT *uptr);
|
||
static int32 vh_rxinta (void);
|
||
static int32 vh_txinta (void);
|
||
static t_stat vh_clear (int32 vh, t_bool flag);
|
||
static t_stat vh_reset (DEVICE *dptr);
|
||
static t_stat vh_attach (UNIT *uptr, char *cptr);
|
||
static t_stat vh_detach (UNIT *uptr);
|
||
static t_stat vh_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
static t_stat vh_show (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
static t_stat vh_show_debug (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
static t_stat vh_show_rbuf (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
static t_stat vh_show_txq (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
static t_stat vh_putc (int32 vh, TMLX *lp, int32 chan, int32 data);
|
||
static void doDMA (int32 vh, int32 chan);
|
||
static t_stat vh_summ (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
|
||
int32 tmxr_send_buffered_data (TMLN *lp);
|
||
|
||
/* SIMH I/O Structures */
|
||
|
||
static DIB vh_dib = {
|
||
IOBA_VH,
|
||
IOLN_VH * VH_MUXES,
|
||
&vh_rd, /* read */
|
||
&vh_wr, /* write */
|
||
2, /* # of vectors */
|
||
IVCL (VHRX),
|
||
VEC_VHRX,
|
||
{ &vh_rxinta, &vh_txinta } /* int. ack. routines */
|
||
};
|
||
|
||
static UNIT vh_unit[VH_MUXES] = {
|
||
{ UDATA (&vh_svc, UNIT_ATTABLE, 0) },
|
||
{ UDATA (&vh_svc, UNIT_ATTABLE, 0) },
|
||
{ UDATA (&vh_svc, UNIT_ATTABLE, 0) },
|
||
{ UDATA (&vh_svc, UNIT_ATTABLE, 0) },
|
||
};
|
||
|
||
static const REG vh_nlreg = { DRDATA (NLINES, vh_desc.lines, 6), PV_LEFT };
|
||
|
||
static const REG vh_reg[] = {
|
||
{ BRDATA (CSR, vh_csr, DEV_RDX, 16, VH_MUXES) },
|
||
{ GRDATA (DEVADDR, vh_dib.ba, DEV_RDX, 32, 0), REG_HRO },
|
||
{ GRDATA (DEVVEC, vh_dib.vec, DEV_RDX, 16, 0), REG_HRO },
|
||
{ NULL }
|
||
};
|
||
|
||
static const MTAB vh_mod[] = {
|
||
{ UNIT_MODEDHU, 0, "DHV mode", "DHV", NULL },
|
||
{ UNIT_MODEDHU, UNIT_MODEDHU, "DHU mode", "DHU", NULL },
|
||
{ UNIT_FASTDMA, 0, NULL, "NORMAL", NULL },
|
||
{ UNIT_FASTDMA, UNIT_FASTDMA, "fast DMA", "FASTDMA", NULL },
|
||
{ UNIT_MODEM, 0, NULL, "NOMODEM", NULL },
|
||
{ UNIT_MODEM, UNIT_MODEM, "modem", "MODEM", NULL },
|
||
{ UNIT_HANGUP, 0, NULL, "NOHANGUP", NULL },
|
||
{ UNIT_HANGUP, UNIT_HANGUP, "hangup", "HANGUP", NULL },
|
||
{ MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS",
|
||
&set_addr, &show_addr, NULL },
|
||
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
|
||
&set_vec, &vh_show_vec, NULL },
|
||
{ MTAB_XTD|MTAB_VDV, 0, NULL, "AUTOCONFIGURE",
|
||
&set_addr_flt, NULL, NULL },
|
||
/* this one is dangerous, don't use yet */
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_VAL, 0, "lines", "LINES",
|
||
NULL, NULL, (REG *)&vh_nlreg },
|
||
{ UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &vh_summ },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
|
||
NULL, &vh_show, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
|
||
NULL, &vh_show, NULL },
|
||
{ MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
|
||
&tmxr_dscln, NULL, &vh_desc },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEBUG", NULL,
|
||
NULL, &vh_show_debug, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "RBUF", NULL,
|
||
NULL, &vh_show_rbuf, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "TXQ", NULL,
|
||
NULL, &vh_show_txq, NULL },
|
||
{ 0 }
|
||
};
|
||
|
||
DEVICE vh_dev = {
|
||
"VH", /* name */
|
||
vh_unit, /* units */
|
||
(REG *)vh_reg, /* registers */
|
||
(MTAB *)vh_mod, /* modifiers */
|
||
VH_MUXES, /* # units */
|
||
DEV_RDX, /* address radix */
|
||
8, /* address width */
|
||
1, /* address increment */
|
||
DEV_RDX, /* data radix */
|
||
8, /* data width */
|
||
NULL, /* examine routine */
|
||
NULL, /* deposit routine */
|
||
&vh_reset, /* reset routine */
|
||
NULL, /* boot routine */
|
||
&vh_attach, /* attach routine */
|
||
&vh_detach, /* detach routine */
|
||
(void *)&vh_dib, /* context */
|
||
DEV_FLTA | DEV_DISABLE | DEV_NET | DEV_QBUS | DEV_UBUS, /* flags */
|
||
};
|
||
|
||
/* Interrupt routines */
|
||
|
||
static void vh_clr_rxint ( int32 vh )
|
||
{
|
||
vh_rxi &= ~(1 << vh);
|
||
if (vh_rxi == 0)
|
||
CLR_INT (VHRX);
|
||
else
|
||
SET_INT (VHRX);
|
||
}
|
||
|
||
static void vh_set_rxint ( int32 vh )
|
||
{
|
||
vh_rxi |= (1 << vh);
|
||
SET_INT (VHRX);
|
||
}
|
||
|
||
/* RX interrupt ack. (bus cycle) */
|
||
|
||
static int32 vh_rxinta (void)
|
||
{
|
||
int32 vh;
|
||
|
||
for (vh = 0; vh < VH_MUXES; vh++) {
|
||
if (vh_rxi & (1 << vh)) {
|
||
vh_clr_rxint (vh);
|
||
return (vh_dib.vec + (vh * 010));
|
||
}
|
||
}
|
||
return (0);
|
||
}
|
||
|
||
static void vh_clr_txint ( int32 vh )
|
||
{
|
||
vh_txi &= ~(1 << vh);
|
||
if (vh_txi == 0)
|
||
CLR_INT (VHTX);
|
||
else
|
||
SET_INT (VHTX);
|
||
}
|
||
|
||
static void vh_set_txint ( int32 vh )
|
||
{
|
||
vh_txi |= (1 << vh);
|
||
SET_INT (VHTX);
|
||
}
|
||
|
||
/* TX interrupt ack. (bus cycle) */
|
||
|
||
static int32 vh_txinta (void)
|
||
{
|
||
int32 vh;
|
||
|
||
for (vh = 0; vh < VH_MUXES; vh++) {
|
||
if (vh_txi & (1 << vh)) {
|
||
vh_clr_txint (vh);
|
||
return (vh_dib.vec + 4 + (vh * 010));
|
||
}
|
||
}
|
||
return (0);
|
||
}
|
||
/* RX FIFO get/put routines */
|
||
|
||
/* return 0 on success, -1 on FIFO overflow */
|
||
|
||
static int32 fifo_put ( int32 vh,
|
||
TMLX *lp,
|
||
int32 data )
|
||
{
|
||
int32 status = 0;
|
||
|
||
if (lp == NULL)
|
||
goto override;
|
||
/* this might have to move to vh_getc() */
|
||
if ((lp->lnctrl & LNCTRL_OAUTO) && ((data & RBUF_DIAG) == 0)) {
|
||
TMLX *l0p;
|
||
/* implement transmitted data flow control */
|
||
switch (data & 0377) {
|
||
case XON:
|
||
lp->tbuf2 |= TB2_TX_ENA;
|
||
goto common;
|
||
case XOFF:
|
||
lp->tbuf2 &= ~TB2_TX_ENA;
|
||
common:
|
||
/* find line 0 for this controller */
|
||
l0p = &vh_parm[vh * VH_LINES];
|
||
if (l0p->lpr & LPR_DISAB_XRPT)
|
||
return (0);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
/* BUG: which of the following 2 is correct? */
|
||
/* if ((data & RBUF_DIAG) == RBUF_DIAG) */
|
||
if (data & RBUF_DIAG)
|
||
goto override;
|
||
if (((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) == 2)
|
||
goto override;
|
||
if (!(lp->lnctrl & LNCTRL_RX_ENA))
|
||
return (0);
|
||
override:
|
||
vh_csr[vh] |= CSR_RX_DATA_AVAIL;
|
||
if (rbuf_idx[vh] < FIFO_SIZE) {
|
||
vh_rbuf[vh][rbuf_idx[vh]] = data;
|
||
rbuf_idx[vh] += 1;
|
||
} else {
|
||
vh_ovrrun[vh] |= (1 << RBUF_GETLINE (data));
|
||
status = -1;
|
||
}
|
||
if (vh_csr[vh] & CSR_RXIE) {
|
||
if (vh_unit[vh].flags & UNIT_MODEDHU) {
|
||
/* was it a modem status change? */
|
||
if ((data & RBUF_DIAG) == RBUF_DIAG)
|
||
vh_set_rxint (vh);
|
||
/* look for FIFO alarm @ 3/4 full */
|
||
else if (rbuf_idx[vh] == FIFO_ALARM)
|
||
vh_set_rxint (vh);
|
||
else if (vh_timer[vh] == 0)
|
||
; /* nothing, infinite timeout */
|
||
else if (vh_timer[vh] == 1)
|
||
vh_set_rxint (vh);
|
||
else if (vh_timeo[vh] == 0)
|
||
vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1;
|
||
} else {
|
||
/* Interrupt on transition _from_ an empty FIFO */
|
||
if (rbuf_idx[vh] == 1)
|
||
vh_set_rxint (vh);
|
||
}
|
||
}
|
||
if (rbuf_idx[vh] > FIFO_ALARM)
|
||
vh_crit |= (1 << vh);
|
||
/* Implement RX FIFO-level flow control */
|
||
if (lp != NULL) {
|
||
if ((lp->lnctrl & LNCTRL_FORCE_XOFF) ||
|
||
((vh_crit & (1 << vh)) && (lp->lnctrl & LNCTRL_IAUTO))) {
|
||
int32 chan = RBUF_GETLINE(data);
|
||
vh_stall[vh] ^= (1 << chan);
|
||
/* send XOFF every other character received */
|
||
if (vh_stall[vh] & (1 << chan))
|
||
vh_putc (vh, lp, chan, XOFF);
|
||
}
|
||
}
|
||
return (status);
|
||
}
|
||
|
||
static int32 fifo_get ( int32 vh )
|
||
{
|
||
int32 data, i;
|
||
|
||
if (rbuf_idx[vh] == 0) {
|
||
vh_csr[vh] &= ~CSR_RX_DATA_AVAIL;
|
||
return (0);
|
||
}
|
||
/* pick off the first character, mark valid */
|
||
data = vh_rbuf[vh][0] | RBUF_DATA_VALID;
|
||
/* move the remainder up */
|
||
rbuf_idx[vh] -= 1;
|
||
for (i = 0; i < rbuf_idx[vh]; i++)
|
||
vh_rbuf[vh][i] = vh_rbuf[vh][i + 1];
|
||
/* rbuf_idx[vh] -= 1; */
|
||
/* look for any previous overruns */
|
||
if (vh_ovrrun[vh]) {
|
||
for (i = 0; i < VH_LINES; i++) {
|
||
if (vh_ovrrun[vh] & (1 << i)) {
|
||
fifo_put (vh, NULL, RBUF_OVERRUN_ERR |
|
||
RBUF_PUTLINE (i));
|
||
vh_ovrrun[vh] &= ~(1 << i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
/* recompute FIFO alarm condition */
|
||
if ((rbuf_idx[vh] < FIFO_HALF) && (vh_crit & (1 << vh))) {
|
||
vh_crit &= ~(1 << vh);
|
||
/* send XON to all XOFF'd channels on this controller */
|
||
for (i = 0; i < VH_LINES; i++) {
|
||
TMLX *lp = &vh_parm[(vh * VH_LINES) + i];
|
||
if (lp->lnctrl & LNCTRL_FORCE_XOFF)
|
||
continue;
|
||
if (vh_stall[vh] & (1 << i)) {
|
||
vh_putc (vh, NULL, i, XON);
|
||
vh_stall[vh] &= ~(1 << i);
|
||
}
|
||
}
|
||
}
|
||
return (data & 0177777);
|
||
}
|
||
/* TX Q manipulation */
|
||
|
||
static int32 dq_tx_report ( int32 vh )
|
||
{
|
||
int32 data, i;
|
||
|
||
if (txq_idx[vh] == 0)
|
||
return (0);
|
||
data = vh_txq[vh][0];
|
||
txq_idx[vh] -= 1;
|
||
for (i = 0; i < txq_idx[vh]; i++)
|
||
vh_txq[vh][i] = vh_txq[vh][i + 1];
|
||
/* txq_idx[vh] -= 1; */
|
||
return (data & 0177777);
|
||
}
|
||
|
||
static void q_tx_report ( int32 vh,
|
||
int32 data )
|
||
{
|
||
if (vh_csr[vh] & CSR_TXIE)
|
||
vh_set_txint (vh);
|
||
if (txq_idx[vh] >= TXQ_SIZE) {
|
||
/* BUG: which of the following 2 is correct? */
|
||
dq_tx_report (vh);
|
||
/* return; */
|
||
}
|
||
vh_txq[vh][txq_idx[vh]] = CSR_TX_ACTION | data;
|
||
txq_idx[vh] += 1;
|
||
}
|
||
/* Channel get/put routines */
|
||
|
||
static void HangupModem ( int32 vh,
|
||
TMLX *lp,
|
||
int32 chan )
|
||
{
|
||
if (vh_unit[vh].flags & UNIT_MODEM)
|
||
lp->lstat &= ~(STAT_DCD|STAT_DSR|STAT_CTS|STAT_RI);
|
||
if (lp->lnctrl & LNCTRL_LINK_TYPE)
|
||
/* RBUF<0> = 0 for modem status */
|
||
fifo_put (vh, lp, RBUF_DIAG |
|
||
RBUF_PUTLINE (chan) |
|
||
((lp->lstat >> 8) & 0376));
|
||
/* BUG: check for overflow above */
|
||
}
|
||
|
||
/* TX a character on a line, regardless of the TX enable state */
|
||
|
||
static t_stat vh_putc ( int32 vh,
|
||
TMLX *lp,
|
||
int32 chan,
|
||
int32 data )
|
||
{
|
||
int32 val;
|
||
t_stat status = SCPE_OK;
|
||
|
||
/* truncate to desired character length */
|
||
data &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) & LPR_M_CHAR_LGTH];
|
||
switch ((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) {
|
||
case 0: /* normal */
|
||
#if 0
|
||
/* check for (external) loopback setting */
|
||
switch (vh_loop[vh]) {
|
||
default:
|
||
case LOOP_NONE:
|
||
break;
|
||
}
|
||
#endif
|
||
status = tmxr_putc_ln (lp->tmln, data);
|
||
if (status == SCPE_LOST) {
|
||
tmxr_reset_ln (lp->tmln);
|
||
HangupModem (vh, lp, chan);
|
||
} else if (status == SCPE_STALL) {
|
||
/* let's flush and try again */
|
||
tmxr_send_buffered_data (lp->tmln);
|
||
status = tmxr_putc_ln (lp->tmln, data);
|
||
}
|
||
break;
|
||
case 1: /* auto echo */
|
||
break;
|
||
case 2: /* local loopback */
|
||
if (lp->lnctrl & LNCTRL_BREAK)
|
||
val = fifo_put (vh, lp,
|
||
RBUF_FRAME_ERR | RBUF_PUTLINE (chan));
|
||
else
|
||
val = fifo_put (vh, lp,
|
||
RBUF_PUTLINE (chan) | data);
|
||
status = (val < 0) ? SCPE_TTMO : SCPE_OK;
|
||
break;
|
||
default: /* remote loopback */
|
||
break;
|
||
}
|
||
return (status);
|
||
}
|
||
|
||
/* Retrieve all stored input from TMXR and place in RX FIFO */
|
||
|
||
static void vh_getc ( int32 vh )
|
||
{
|
||
uint32 i, c;
|
||
TMLX *lp;
|
||
|
||
for (i = 0; i < VH_LINES; i++) {
|
||
lp = &vh_parm[(vh * VH_LINES) + i];
|
||
while (c = tmxr_getc_ln (lp->tmln)) {
|
||
if (c & SCPE_BREAK) {
|
||
fifo_put (vh, lp,
|
||
RBUF_FRAME_ERR | RBUF_PUTLINE (i));
|
||
/* BUG: check for overflow above */
|
||
} else {
|
||
c &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) &
|
||
LPR_M_CHAR_LGTH];
|
||
fifo_put (vh, lp, RBUF_PUTLINE (i) | c);
|
||
/* BUG: check for overflow above */
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* I/O dispatch routines */
|
||
|
||
static t_stat vh_rd ( int32 *data,
|
||
int32 PA,
|
||
int32 access )
|
||
{
|
||
int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line;
|
||
TMLX *lp;
|
||
|
||
switch ((PA >> 1) & 7) {
|
||
case 0: /* CSR */
|
||
*data = vh_csr[vh] | dq_tx_report (vh);
|
||
vh_csr[vh] &= ~0117400; /* clear the read-once bits */
|
||
break;
|
||
case 1: /* RBUF */
|
||
*data = fifo_get (vh);
|
||
break;
|
||
case 2: /* LPR */
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {
|
||
*data = 0;
|
||
break;
|
||
}
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
*data = lp->lpr;
|
||
break;
|
||
case 3: /* STAT/FIFOSIZE */
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {
|
||
*data = 0;
|
||
break;
|
||
}
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
*data = (lp->lstat & ~0377) | /* modem status */
|
||
#if 0
|
||
(64 - tmxr_tqln (lp->tmln));
|
||
fprintf (stderr, "\rtqln %d\n", 64 - tmxr_tqln (lp->tmln));
|
||
#else
|
||
64;
|
||
#endif
|
||
break;
|
||
case 4: /* LNCTRL */
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {
|
||
*data = 0;
|
||
break;
|
||
}
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
*data = lp->lnctrl;
|
||
break;
|
||
case 5: /* TBUFFAD1 */
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {
|
||
*data = 0;
|
||
break;
|
||
}
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
*data = lp->tbuf1;
|
||
break;
|
||
case 6: /* TBUFFAD2 */
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {
|
||
*data = 0;
|
||
break;
|
||
}
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
*data = lp->tbuf2;
|
||
break;
|
||
case 7: /* TBUFFCT */
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {
|
||
*data = 0;
|
||
break;
|
||
}
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
*data = lp->tbuffct;
|
||
break;
|
||
default:
|
||
/* can't happen */
|
||
break;
|
||
}
|
||
return (SCPE_OK);
|
||
}
|
||
|
||
static t_stat vh_wr ( int32 data,
|
||
int32 PA,
|
||
int32 access )
|
||
{
|
||
int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line;
|
||
TMLX *lp;
|
||
|
||
switch ((PA >> 1) & 7) {
|
||
case 0: /* CSR, but no read-modify-write */
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(vh_csr[vh] & 0377) | (data << 8) :
|
||
(vh_csr[vh] & ~0377) | data & 0377;
|
||
if (data & CSR_MASTER_RESET) {
|
||
if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP))
|
||
data &= ~CSR_MASTER_RESET;
|
||
sim_activate (&vh_unit[vh], tmxr_poll);
|
||
/* vh_mcount[vh] = 72; */ /* 1.2 seconds */
|
||
vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */
|
||
}
|
||
if ((data & CSR_RXIE) == 0)
|
||
vh_clr_rxint (vh);
|
||
/* catch the RXIE transition if the FIFO is not empty */
|
||
else if (((vh_csr[vh] & CSR_RXIE) == 0) &&
|
||
(rbuf_idx[vh] != 0)) {
|
||
if (vh_unit[vh].flags & UNIT_MODEDHU) {
|
||
if (rbuf_idx[vh] > FIFO_ALARM)
|
||
vh_set_rxint (vh);
|
||
else if (vh_timer[vh] == 0)
|
||
;
|
||
else if (vh_timer[vh] == 1)
|
||
vh_set_rxint (vh);
|
||
else if (vh_timeo[vh] == 0)
|
||
vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1;
|
||
} else {
|
||
vh_set_rxint (vh);
|
||
}
|
||
}
|
||
if ((data & CSR_TXIE) == 0)
|
||
vh_clr_txint (vh);
|
||
else if (((vh_csr[vh] & CSR_TXIE) == 0) &&
|
||
(txq_idx[vh] != 0))
|
||
vh_set_txint (vh);
|
||
vh_csr[vh] = (vh_csr[vh] & ~((uint16) CSR_RW)) | (data & (uint16) CSR_RW);
|
||
break;
|
||
case 1: /* TXCHAR/RXTIMER */
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
|
||
break;
|
||
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
|
||
vh_mcount[vh] = 1;
|
||
break;
|
||
}
|
||
if (vh_unit[vh].flags & UNIT_MODEDHU) {
|
||
if (CSR_GETCHAN (vh_csr[vh]) != 0)
|
||
break;
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(vh_timer[vh] & 0377) | (data << 8) :
|
||
(vh_timer[vh] & ~0377) | (data & 0377);
|
||
vh_timer[vh] = data & 0377;
|
||
#if 0
|
||
if (vh_csr[vh] & CSR_RXIE) {
|
||
if (rbuf_idx[vh] > FIFO_ALARM)
|
||
vh_set_rxint (vh);
|
||
else if (vh_timer[vh] == 0)
|
||
;
|
||
else if (vh_timer[vh] == 1)
|
||
vh_set_rxint (vh);
|
||
else if (vh_timeo[vh] == 0)
|
||
vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1;
|
||
}
|
||
#endif
|
||
} else {
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(lp->txchar & 0377) | (data << 8) :
|
||
(lp->txchar & ~0377) | (data & 0377);
|
||
lp->txchar = data; /* TXCHAR */
|
||
if (lp->txchar & TXCHAR_TX_DATA_VALID) {
|
||
if (lp->tbuf2 & TB2_TX_ENA)
|
||
vh_putc (vh, lp,
|
||
CSR_GETCHAN (vh_csr[vh]),
|
||
lp->txchar);
|
||
q_tx_report (vh,
|
||
CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);
|
||
lp->txchar &= ~TXCHAR_TX_DATA_VALID;
|
||
}
|
||
}
|
||
break;
|
||
case 2: /* LPR */
|
||
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
|
||
vh_mcount[vh] = 1;
|
||
break;
|
||
}
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
|
||
break;
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(lp->lpr & 0377) | (data << 8) :
|
||
(lp->lpr & ~0377) | data & 0377;
|
||
/* Modify only if CSR<3:0> == 0 */
|
||
if (CSR_GETCHAN (vh_csr[vh]) != 0)
|
||
data &= ~LPR_DISAB_XRPT;
|
||
lp->lpr = data;
|
||
if (((lp->lpr >> LPR_V_DIAG) & LPR_M_DIAG) == 1) {
|
||
fifo_put (vh, lp,
|
||
RBUF_DIAG |
|
||
RBUF_PUTLINE (CSR_GETCHAN (vh_csr[vh])) |
|
||
BMP_OK);
|
||
/* BUG: check for overflow above */
|
||
lp->lpr &= ~(LPR_M_DIAG << LPR_V_DIAG);
|
||
}
|
||
break;
|
||
case 3: /* STAT/FIFODATA */
|
||
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
|
||
vh_mcount[vh] = 1;
|
||
break;
|
||
}
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
|
||
break;
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
if (vh_unit[vh].flags & UNIT_MODEDHU) {
|
||
/* high byte writes not allowed */
|
||
if (PA & 1)
|
||
break;
|
||
/* transmit 1 or 2 characters */
|
||
if (!(lp->tbuf2 & TB2_TX_ENA))
|
||
break;
|
||
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), data);
|
||
q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);
|
||
if (access != WRITEB)
|
||
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]),
|
||
data >> 8);
|
||
}
|
||
break;
|
||
case 4: /* LNCTRL */
|
||
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
|
||
vh_mcount[vh] = 1;
|
||
break;
|
||
}
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
|
||
break;
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(lp->lnctrl & 0377) | (data << 8) :
|
||
(lp->lnctrl & ~0377) | data & 0377;
|
||
/* catch the abort TX transition */
|
||
if (!(lp->lnctrl & LNCTRL_TX_ABORT) &&
|
||
(data & LNCTRL_TX_ABORT)) {
|
||
if ((lp->tbuf2 & TB2_TX_ENA) &&
|
||
(lp->tbuf2 & TB2_TX_DMA_START)) {
|
||
lp->tbuf2 &= ~TB2_TX_DMA_START;
|
||
q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);
|
||
}
|
||
}
|
||
/* Implement program-initiated flow control */
|
||
if ( (data & LNCTRL_FORCE_XOFF) &&
|
||
!(lp->lnctrl & LNCTRL_FORCE_XOFF) ) {
|
||
if (!(lp->lnctrl & LNCTRL_IAUTO))
|
||
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XOFF);
|
||
} else if ( !(data & LNCTRL_FORCE_XOFF) &&
|
||
(lp->lnctrl & LNCTRL_FORCE_XOFF) ) {
|
||
if (!(lp->lnctrl & LNCTRL_IAUTO))
|
||
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON);
|
||
else if (!(vh_crit & (1 << vh)) &&
|
||
(vh_stall[vh] & (1 << CSR_GETCHAN (vh_csr[vh]))))
|
||
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON);
|
||
}
|
||
if ( (data & LNCTRL_IAUTO) && /* IAUTO 0->1 */
|
||
!(lp->lnctrl & LNCTRL_IAUTO) ) {
|
||
if (!(lp->lnctrl & LNCTRL_FORCE_XOFF)) {
|
||
if (vh_crit & (1 << vh)) {
|
||
vh_putc (vh, lp,
|
||
CSR_GETCHAN (vh_csr[vh]), XOFF);
|
||
vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh]));
|
||
}
|
||
} else {
|
||
/* vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh])) */;
|
||
}
|
||
} else if ( !(data & LNCTRL_IAUTO) &&
|
||
(lp->lnctrl & LNCTRL_IAUTO) ) {
|
||
if (!(lp->lnctrl & LNCTRL_FORCE_XOFF))
|
||
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON);
|
||
}
|
||
/* check modem control bits */
|
||
if ( !(data & LNCTRL_DTR) && /* DTR 1->0 */
|
||
(lp->lnctrl & LNCTRL_DTR)) {
|
||
if ((lp->tmln->conn) && (vh_unit[vh].flags & UNIT_HANGUP)) {
|
||
tmxr_linemsg (lp->tmln, "\r\nLine hangup\r\n");
|
||
tmxr_reset_ln (lp->tmln);
|
||
}
|
||
HangupModem (vh, lp, CSR_GETCHAN (vh_csr[vh]));
|
||
}
|
||
lp->lnctrl = data;
|
||
lp->tmln->rcve = (data & LNCTRL_RX_ENA) ? 1 : 0;
|
||
tmxr_poll_rx (&vh_desc);
|
||
vh_getc (vh);
|
||
if (lp->lnctrl & LNCTRL_BREAK)
|
||
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), 0);
|
||
break;
|
||
case 5: /* TBUFFAD1 */
|
||
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
|
||
vh_mcount[vh] = 1;
|
||
break;
|
||
}
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
|
||
break;
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(lp->tbuf1 & 0377) | (data << 8) :
|
||
(lp->tbuf1 & ~0377) | data & 0377;
|
||
lp->tbuf1 = data;
|
||
break;
|
||
case 6: /* TBUFFAD2 */
|
||
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
|
||
vh_mcount[vh] = 1;
|
||
break;
|
||
}
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
|
||
break;
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(lp->tbuf2 & 0377) | (data << 8) :
|
||
(lp->tbuf2 & ~0377) | data & 0377;
|
||
lp->tbuf2 = data;
|
||
/* if starting a DMA, clear DMA_ERR */
|
||
if (vh_unit[vh].flags & UNIT_FASTDMA) {
|
||
doDMA (vh, CSR_GETCHAN (vh_csr[vh]));
|
||
tmxr_send_buffered_data (lp->tmln);
|
||
}
|
||
break;
|
||
case 7: /* TBUFFCT */
|
||
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
|
||
vh_mcount[vh] = 1;
|
||
break;
|
||
}
|
||
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
|
||
break;
|
||
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
|
||
lp = &vh_parm[line];
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(lp->tbuffct & 0377) | (data << 8) :
|
||
(lp->tbuffct & ~0377) | data & 0377;
|
||
lp->tbuffct = data;
|
||
break;
|
||
default:
|
||
/* can't happen */
|
||
break;
|
||
}
|
||
return (SCPE_OK);
|
||
}
|
||
|
||
static void doDMA ( int32 vh,
|
||
int32 chan )
|
||
{
|
||
int32 line, status;
|
||
uint32 pa;
|
||
TMLX *lp;
|
||
|
||
line = (vh * VH_LINES) + chan;
|
||
lp = &vh_parm[line];
|
||
if ((lp->tbuf2 & TB2_TX_ENA) && (lp->tbuf2 & TB2_TX_DMA_START)) {
|
||
/* BUG: should compare against available xmit buffer space */
|
||
pa = lp->tbuf1;
|
||
pa |= (lp->tbuf2 & TB2_M_TBUFFAD) << 16;
|
||
status = chan << CSR_V_TX_LINE;
|
||
while (lp->tbuffct) {
|
||
uint8 buf;
|
||
if (Map_ReadB (pa, 1, &buf)) {
|
||
status |= CSR_TX_DMA_ERR;
|
||
lp->tbuffct = 0;
|
||
break;
|
||
}
|
||
if (vh_putc (vh, lp, chan, buf) != SCPE_OK)
|
||
break;
|
||
/* pa = (pa + 1) & PAMASK; */
|
||
pa = (pa + 1) & ((1 << 22) - 1);
|
||
lp->tbuffct--;
|
||
}
|
||
lp->tbuf1 = pa & 0177777;
|
||
lp->tbuf2 = (lp->tbuf2 & ~TB2_M_TBUFFAD) |
|
||
((pa >> 16) & TB2_M_TBUFFAD);
|
||
if (lp->tbuffct == 0) {
|
||
lp->tbuf2 &= ~TB2_TX_DMA_START;
|
||
q_tx_report (vh, status);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Perform many of the functions of PROC2 */
|
||
|
||
static t_stat vh_svc ( UNIT *uptr )
|
||
{
|
||
int32 vh, newln, i;
|
||
|
||
/* scan all muxes for countdown reset */
|
||
for (vh = 0; vh < VH_MUXES; vh++) {
|
||
if (vh_csr[vh] & CSR_MASTER_RESET) {
|
||
if (vh_mcount[vh] != 0)
|
||
vh_mcount[vh] -= 1;
|
||
else
|
||
vh_clear (vh, FALSE);
|
||
}
|
||
}
|
||
/* sample every 10ms for modem changes (new connections) */
|
||
newln = tmxr_poll_conn (&vh_desc);
|
||
if (newln >= 0) {
|
||
TMLX *lp;
|
||
int32 line;
|
||
vh = newln / VH_LINES; /* determine which mux */
|
||
line = newln - (vh * VH_LINES);
|
||
lp = &vh_parm[newln];
|
||
lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS;
|
||
if (!(lp->lnctrl & LNCTRL_DTR))
|
||
lp->lstat |= STAT_RI;
|
||
if (lp->lnctrl & LNCTRL_LINK_TYPE)
|
||
fifo_put (vh, lp, RBUF_DIAG |
|
||
RBUF_PUTLINE (line) |
|
||
((lp->lstat >> 8) & 0376));
|
||
/* BUG: should check for overflow above */
|
||
}
|
||
/* scan all muxes, lines for DMA to complete; start every 3.12ms */
|
||
for (vh = 0; vh < VH_MUXES; vh++) {
|
||
for (i = 0; i < VH_LINES; i++)
|
||
doDMA (vh, i);
|
||
}
|
||
/* interrupt driven in a real DHQ */
|
||
tmxr_poll_rx (&vh_desc);
|
||
for (vh = 0; vh < VH_MUXES; vh++)
|
||
vh_getc (vh);
|
||
tmxr_poll_tx (&vh_desc);
|
||
/* scan all DHU-mode muxes for RX FIFO timeout */
|
||
for (vh = 0; vh < VH_MUXES; vh++) {
|
||
if (vh_unit[vh].flags & UNIT_MODEDHU) {
|
||
if (vh_timeo[vh] && (vh_csr[vh] & CSR_RXIE)) {
|
||
vh_timeo[vh] -= 1;
|
||
if ((vh_timeo[vh] == 0) && rbuf_idx[vh])
|
||
vh_set_rxint (vh);
|
||
}
|
||
}
|
||
}
|
||
sim_activate (uptr, tmxr_poll); /* requeue ourselves */
|
||
return (SCPE_OK);
|
||
}
|
||
|
||
/* init a channel on a controller */
|
||
|
||
/* set for:
|
||
send/receive 9600
|
||
8 data bits
|
||
1 stop bit
|
||
no parity
|
||
parity odd
|
||
auto-flow off
|
||
RX disabled
|
||
TX enabled
|
||
no break on line
|
||
no loopback
|
||
link type set to data-leads only
|
||
DTR & RTS off
|
||
DMA character counter 0
|
||
DMA start address registers 0
|
||
TX_DMA_START 0
|
||
TX_ABORT 0
|
||
auto-flow reports enabled
|
||
FIFO size set to 64
|
||
*/
|
||
|
||
static void vh_init_chan ( int32 vh,
|
||
int32 chan )
|
||
{
|
||
int32 line;
|
||
TMLX *lp;
|
||
|
||
line = (vh * VH_LINES) + chan;
|
||
lp = &vh_parm[line];
|
||
lp->lpr = (RATE_9600 << LPR_V_TX_SPEED) |
|
||
(RATE_9600 << LPR_V_RX_SPEED) |
|
||
(03 << LPR_V_CHAR_LGTH);
|
||
lp->lnctrl = 0;
|
||
lp->lstat &= ~(STAT_MDL | STAT_DHUID | STAT_RI);
|
||
if (vh_unit[vh].flags & UNIT_MODEDHU)
|
||
lp->lstat |= STAT_DHUID | 64;
|
||
if (!(vh_unit[vh].flags & UNIT_MODEM))
|
||
lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS;
|
||
lp->tmln->xmte = 1;
|
||
lp->tmln->rcve = 0;
|
||
lp->tbuffct = 0;
|
||
lp->tbuf1 = 0;
|
||
lp->tbuf2 = TB2_TX_ENA;
|
||
lp->txchar = 0;
|
||
}
|
||
|
||
/* init a controller; flag true if BINIT, false if master.reset */
|
||
|
||
static t_stat vh_clear ( int32 vh,
|
||
t_bool flag )
|
||
{
|
||
int32 i;
|
||
|
||
txq_idx[vh] = 0;
|
||
rbuf_idx[vh] = 0;
|
||
/* put 8 diag bytes in FIFO: 6 SELF_x, 2 circuit revision codes */
|
||
if (vh_csr[vh] & CSR_SKIP) {
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_SKIP);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_SKIP);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_SKIP);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_SKIP);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_SKIP);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_SKIP);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105);
|
||
} else {
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_NULL);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_NULL);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_NULL);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_NULL);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_NULL);
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_NULL);
|
||
/* PROC2 ver. 1 */
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107);
|
||
/* PROC1 ver. 1 */
|
||
fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105);
|
||
}
|
||
vh_csr[vh] &= ~(CSR_TX_ACTION|CSR_DIAG_FAIL|CSR_MASTER_RESET);
|
||
if (flag)
|
||
vh_csr[vh] &= ~(CSR_TXIE|CSR_RXIE|CSR_SKIP);
|
||
vh_csr[vh] |= CSR_TX_DMA_ERR | (CSR_M_TX_LINE << CSR_V_TX_LINE);
|
||
vh_clr_rxint (vh);
|
||
vh_clr_txint (vh);
|
||
vh_timer[vh] = 1;
|
||
vh_timeo[vh] = 0;
|
||
vh_ovrrun[vh] = 0;
|
||
for (i = 0; i < VH_LINES; i++)
|
||
vh_init_chan (vh, i);
|
||
vh_crit &= ~(1 << vh);
|
||
vh_stall[vh] = 0;
|
||
vh_loop[vh] = LOOP_NONE;
|
||
return (SCPE_OK);
|
||
}
|
||
|
||
/* Reset all controllers. Used by BINIT and RESET. */
|
||
|
||
static t_stat vh_reset ( DEVICE *dptr )
|
||
{
|
||
int32 i;
|
||
|
||
for (i = 0; i < (VH_MUXES * VH_LINES); i++)
|
||
vh_parm[i].tmln = &vh_ldsc[i];
|
||
for (i = 0; i < VH_MUXES; i++) {
|
||
#if defined (VM_PDP11)
|
||
/* if Unibus, force DHU mode */
|
||
if (UNIBUS)
|
||
vh_unit[i].flags |= UNIT_MODEDHU;
|
||
#endif
|
||
vh_clear (i, TRUE);
|
||
}
|
||
vh_rxi = vh_txi = 0;
|
||
CLR_INT (VHRX);
|
||
CLR_INT (VHTX);
|
||
for (i = 0; i < VH_MUXES; i++)
|
||
sim_cancel (&vh_unit[i]);
|
||
return (auto_config (RANK_VH, (dptr->flags & DEV_DIS) ? 0 : VH_MUXES));
|
||
}
|
||
|
||
|
||
static t_stat vh_attach ( UNIT *uptr,
|
||
char *cptr )
|
||
{
|
||
if (uptr == &vh_unit[0])
|
||
return (tmxr_attach (&vh_desc, uptr, cptr));
|
||
return (SCPE_NOATT);
|
||
}
|
||
|
||
static t_stat vh_detach ( UNIT *uptr )
|
||
{
|
||
return (tmxr_detach (&vh_desc, uptr));
|
||
}
|
||
|
||
static t_stat vh_summ ( FILE *st,
|
||
UNIT *uptr,
|
||
int32 val,
|
||
void *desc )
|
||
{
|
||
int32 i, t;
|
||
|
||
for (i = t = 0; i < vh_desc.lines; i++) { /* get num conn */
|
||
if (vh_ldsc[i].conn) t = t + 1; }
|
||
fprintf (st, "%d %s", t, (t == 1) ? "connection" : "connections");
|
||
return SCPE_OK;
|
||
}
|
||
|
||
static t_stat vh_show (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||
{
|
||
int32 i, t;
|
||
|
||
for (i = t = 0; i < vh_desc.lines; i++) { /* loop thru conn */
|
||
if (vh_ldsc[i].conn) {
|
||
t = 1;
|
||
if (val) tmxr_fconns (st, &vh_ldsc[i], i);
|
||
else tmxr_fstats (st, &vh_ldsc[i], i); } }
|
||
if (t == 0) fprintf (st, "all disconnected\n");
|
||
return SCPE_OK;
|
||
}
|
||
|
||
static t_stat vh_show_vec ( FILE *st,
|
||
UNIT *uptr,
|
||
int32 val,
|
||
void *desc )
|
||
{
|
||
return (show_vec (st, uptr, val, desc));
|
||
}
|
||
|
||
static void debug_line ( FILE *st,
|
||
int32 vh,
|
||
int32 chan )
|
||
{
|
||
int32 line;
|
||
TMLX *lp;
|
||
|
||
line = (vh * VH_LINES) + chan;
|
||
lp = &vh_parm[line];
|
||
fprintf (st, "\tline %d\tlpr %06o, lnctrl %06o, lstat %06o\n",
|
||
chan, lp->lpr, lp->lnctrl, lp->lstat);
|
||
fprintf (st, "\t\ttbuffct %06o, tbuf1 %06o, tbuf2 %06o, txchar %06o\n",
|
||
lp->tbuffct, lp->tbuf1, lp->tbuf2, lp->txchar);
|
||
fprintf (st, "\t\ttmln rcve %d xmte %d\n",
|
||
lp->tmln->rcve, lp->tmln->xmte);
|
||
}
|
||
|
||
static t_stat vh_show_debug ( FILE *st,
|
||
UNIT *uptr,
|
||
int32 val,
|
||
void *desc )
|
||
{
|
||
int32 i, j;
|
||
|
||
fprintf (st, "VH:\trxi %d, txi %d\n", vh_rxi, vh_txi);
|
||
for (i = 0; i < VH_MUXES; i++) {
|
||
fprintf (st, "VH%d:\tmode %s, crit %d\n", i,
|
||
vh_unit[i].flags & UNIT_MODEDHU ? "DHU" : "DHV",
|
||
vh_crit & (1 << i));
|
||
fprintf (st, "\tCSR %06o, mcount %d, rbuf_idx %d, txq_idx %d\n",
|
||
vh_csr[i], vh_mcount[i], rbuf_idx[i], txq_idx[i]);
|
||
for (j = 0; j < VH_LINES; j++)
|
||
debug_line (st, i, j);
|
||
}
|
||
return (SCPE_OK);
|
||
}
|
||
|
||
static t_stat vh_show_rbuf ( FILE *st,
|
||
UNIT *uptr,
|
||
int32 val,
|
||
void *desc )
|
||
{
|
||
int32 i;
|
||
|
||
for (i = 0; i < rbuf_idx[0]; i++)
|
||
fprintf (st, "%03d: %06o\n", i, vh_rbuf[0][i]);
|
||
return (SCPE_OK);
|
||
}
|
||
|
||
static t_stat vh_show_txq ( FILE *st,
|
||
UNIT *uptr,
|
||
int32 val,
|
||
void *desc )
|
||
{
|
||
int32 i;
|
||
|
||
for (i = 0; i < txq_idx[0]; i++)
|
||
fprintf (st, "%02d: %06o\n\r", i, vh_txq[0][i]);
|
||
return (SCPE_OK);
|
||
}
|
||
|