RESTRICTION: The PDP-15 FPP is only partially debugged. Do NOT enable this feature for normal operations. WARNING: The core simulator files (scp.c, sim_*.c) have been reorganized. Unzip V3.2-0 to an empty directory before attempting to compile the source. IMPORTANT: If you are compiling for UNIX, please read the notes for Ethernet very carefully. You may need to download a new version of the pcap library, or make changes to the makefile, to get Ethernet support to work. 1. New Features in 3.2-0 1.1 SCP and libraries - Added SHOW <device> RADIX command. - Added SHOW <device> MODIFIERS command. - Added SHOW <device> NAMES command. - Added SET/SHOW <device> DEBUG command. - Added sim_vm_parse_addr and sim_vm_fprint_addr optional interfaces. - Added REG_VMAD flag. - Split SCP into separate libraries for easier modification. - Added more room to the device and unit flag fields. - Changed terminal multiplexor library to support unlimited. number of async lines. 1.2 All DECtapes - Added STOP_EOR flag to enable end-of-reel error stop - Added device debug support. 1.3 Nova and Eclipse - Added QTY and ALM multiplexors (Bruce Ray). 1.4 LGP-30 - Added LGP-30/LGP-21 simulator. 1.5 PDP-11 - Added format, address increment inhibit, transfer overrun detection to RK. - Added device debug support to HK, RP, TM, TQ, TS. - Added DEUNA/DELUA (XU) support (Dave Hittner). - Add DZ per-line logging. 1.6 18b PDP's - Added support for 1-4 (PDP-9)/1-16 (PDP-15) additional terminals. 1.7 PDP-10 - Added DEUNA/DELUA (XU) support (Dave Hittner). 1.8 VAX - Added extended memory to 512MB (Mark Pizzolato). - Added RXV21 support. 2. Bugs Fixed in 3.2-0 2.1 SCP - Fixed double logging of SHOW BREAK (found by Mark Pizzolato). - Fixed implementation of REG_VMIO. 2.2 Nova and Eclipse - Fixed device enable/disable support (found by Bruce Ray). 2.3 PDP-1 - Fixed bug in LOAD (found by Mark Crispin). 2.4 PDP-10 - Fixed bug in floating point unpack. - Fixed bug in FIXR (found by Phil Stone, fixed by Chris Smith). 2.6 PDP-11 - Fixed bug in RQ interrupt control (found by Tom Evans). 2.6 PDP-18B - Fixed bug in PDP-15 XVM g_mode implementation. - Fixed bug in PDP-15 indexed address calculation. - Fixed bug in PDP-15 autoindexed address calculation. - Fixed bugs in FPP-15 instruction decode. - Fixed clock response to CAF. - Fixed bug in hardware read-in mode bootstrap. - Fixed PDP-15 XVM instruction decoding errors. 2.7 VAX - Fixed PC read fault in EXTxV. - Fixed PC write fault in INSV.
886 lines
31 KiB
C
886 lines
31 KiB
C
/* sds_io.c: SDS 940 I/O simulator
|
||
|
||
Copyright (c) 2001-2004, 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.
|
||
*/
|
||
|
||
#include "sds_defs.h"
|
||
|
||
/* Data chain word */
|
||
|
||
#define CHD_INT 040 /* int on chain */
|
||
#define CHD_PAGE 037 /* new page # */
|
||
|
||
/* Interlace POT */
|
||
|
||
#define CHI_V_WC 14 /* word count */
|
||
#define CHI_M_WC 01777
|
||
#define CHI_GETWC(x) (((x) >> CHI_V_WC) & CHI_M_WC)
|
||
#define CHI_V_MA 0 /* mem address */
|
||
#define CHI_M_MA 037777
|
||
#define CHI_GETMA(x) (((x) >> CHI_V_MA) & CHI_M_MA)
|
||
|
||
/* System interrupt POT */
|
||
|
||
#define SYI_V_GRP 18 /* group */
|
||
#define SYI_M_GRP 077
|
||
#define SYI_GETGRP(x) (((x) >> SYI_V_GRP) & SYI_M_GRP)
|
||
#define SYI_DIS (1 << 17) /* disarm if 0 */
|
||
#define SYI_ARM (1 << 16) /* arm if 1 */
|
||
#define SYI_M_INT 0177777 /* interrupt */
|
||
|
||
/* Pseudo-device number for EOM/SKS mode 3 */
|
||
|
||
#define I_GETDEV3(x) ((((x) & 020046000) != 020046000)? ((x) & DEV_MASK): DEV_MASK)
|
||
|
||
#define TST_XFR(d,c) (xfr_req && dev_map[d][c])
|
||
#define SET_XFR(d,c) xfr_req = xfr_req | dev_map[d][c]
|
||
#define CLR_XFR(d,c) xfr_req = xfr_req & ~dev_map[d][c]
|
||
#define INV_DEV(d,c) (dev_dsp[d][c] == NULL)
|
||
#define VLD_DEV(d,c) (dev_dsp[d][c] != NULL)
|
||
#define TST_EOR(c) (chan_flag[c] & CHF_EOR)
|
||
#define QAILCE(a) (((a) >= POT_ILCY) && ((a) < (POT_ILCY + NUM_CHAN)))
|
||
|
||
uint8 chan_uar[NUM_CHAN]; /* unit addr */
|
||
uint16 chan_wcr[NUM_CHAN]; /* word count */
|
||
uint16 chan_mar[NUM_CHAN]; /* mem addr */
|
||
uint8 chan_dcr[NUM_CHAN]; /* data chain */
|
||
uint32 chan_war[NUM_CHAN]; /* word assembly */
|
||
uint8 chan_cpw[NUM_CHAN]; /* char per word */
|
||
uint8 chan_cnt[NUM_CHAN]; /* char count */
|
||
uint16 chan_mode[NUM_CHAN]; /* mode */
|
||
uint16 chan_flag[NUM_CHAN]; /* flags */
|
||
static const char *chname[NUM_CHAN] =
|
||
{ "W", "Y", "C", "D", "E", "F", "G", "H" };
|
||
|
||
extern uint32 M[MAXMEMSIZE]; /* memory */
|
||
extern uint32 int_req; /* int req */
|
||
extern uint32 xfr_req; /* xfer req */
|
||
extern uint32 alert; /* pin/pot alert */
|
||
extern uint32 X, EM2, EM3, OV, ion, bpt;
|
||
extern uint32 nml_mode, usr_mode, rtc_pie;
|
||
extern int32 stop_invins, stop_invdev, stop_inviop;
|
||
extern int32 mon_usr_trap;
|
||
extern UNIT cpu_unit;
|
||
extern FILE *sim_log;
|
||
extern DEVICE *sim_devices[];
|
||
|
||
t_stat chan_reset (DEVICE *dptr);
|
||
t_stat chan_read (int32 ch);
|
||
t_stat chan_write (int32 ch);
|
||
void chan_write_mem (int32 ch);
|
||
void chan_flush_war (int32 ch);
|
||
uint32 chan_mar_inc (int32 ch);
|
||
t_stat chan_eor (int32 ch);
|
||
t_stat pot_ilc (uint32 num, uint32 *dat);
|
||
t_stat pot_dcr (uint32 num, uint32 *dat);
|
||
t_stat pin_adr (uint32 num, uint32 *dat);
|
||
t_stat pot_fork (uint32 num, uint32 *dat);
|
||
t_stat dev_disc (uint32 ch, uint32 dev);
|
||
t_stat dev_wreor (uint32 ch, uint32 dev);
|
||
extern t_stat pot_RL1 (uint32 num, uint32 *dat);
|
||
extern t_stat pot_RL2 (uint32 num, uint32 *dat);
|
||
extern t_stat pot_RL4 (uint32 num, uint32 *dat);
|
||
extern t_stat pin_rads (uint32 num, uint32 *dat);
|
||
extern t_stat pot_rada (uint32 num, uint32 *dat);
|
||
extern t_stat pin_dsk (uint32 num, uint32 *dat);
|
||
extern t_stat pot_dsk (uint32 num, uint32 *dat);
|
||
t_stat pin_mux (uint32 num, uint32 *dat);
|
||
t_stat pot_mux (uint32 num, uint32 *dat);
|
||
extern void set_dyn_map (void);
|
||
|
||
/* SDS I/O model
|
||
|
||
A device is modeled by its interactions with a channel. Devices can only be
|
||
accessed via channels. Each channel has its own device address space. This
|
||
means devices can only be accessed from a specific channel.
|
||
|
||
I/O operations start with a channel connect. The EOM instruction is passed
|
||
to the device via the conn routine. This routine is also used for non-channel
|
||
EOM's to the device. For channel connects, the device must remember the
|
||
channel number.
|
||
|
||
The device responds (after a delay) by setting its XFR_RDY flag. This causes
|
||
the channel to invoke either the read or write routine (for input or output)
|
||
to get or put the next character. If the device is an asynchronous output
|
||
device, it calls routine chan_set_ordy to see if there is output available.
|
||
If there is, XFR_RDY is set; if not, the channel is marked to wake the
|
||
attached device when output is available. This prevents invalid rate errors.
|
||
|
||
Output may be terminated by a write end of record, a disconnect, or both.
|
||
Write end of record occurs when the word count reaches zero on an IORD or IORP
|
||
operation. It also occurs if a TOP instruction is issued. The device is
|
||
expected to respond by setting the end of record indicator in the channel,
|
||
which will in turn trigger an end of record interrupt.
|
||
|
||
When the channel operation completes, the channel disconnects and calls the
|
||
disconnect processor to perform any device specific cleanup. The differences
|
||
between write end of record and disconnect are subtle. On magtape output,
|
||
for example, both signal end of record; but write end of record allows the
|
||
magtape to continue moving, while disconnect halts its motion.
|
||
|
||
Valid devices supply a routine to handle potentially all I/O operations
|
||
(connect, disconnect, read, write, write end of record, sks). There are
|
||
separate routines for PIN and POT.
|
||
|
||
Channels could, optionally, handle 12b or 24b characters. The simulator can
|
||
support all widths.
|
||
*/
|
||
|
||
t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
|
||
struct aldisp {
|
||
t_stat (*pin) (uint32 num, uint32 *dat); /* altnum, *dat */
|
||
t_stat (*pot) (uint32 num, uint32 *dat); /* altnum, *dat */
|
||
};
|
||
|
||
/* Channel data structures
|
||
|
||
chan_dev channel device descriptor
|
||
chan_unit channel unit descriptor
|
||
chan_reg channel register list
|
||
*/
|
||
|
||
UNIT chan_unit = { UDATA (NULL, 0, 0) };
|
||
|
||
REG chan_reg[] = {
|
||
{ BRDATA (UAR, chan_uar, 8, 6, NUM_CHAN) },
|
||
{ BRDATA (WCR, chan_wcr, 8, 15, NUM_CHAN) },
|
||
{ BRDATA (MAR, chan_mar, 8, 16, NUM_CHAN) },
|
||
{ BRDATA (DCR, chan_dcr, 8, 6, NUM_CHAN) },
|
||
{ BRDATA (WAR, chan_war, 8, 24, NUM_CHAN) },
|
||
{ BRDATA (CPW, chan_cpw, 8, 2, NUM_CHAN) },
|
||
{ BRDATA (CNT, chan_cnt, 8, 3, NUM_CHAN) },
|
||
{ BRDATA (MODE, chan_mode, 8, 12, NUM_CHAN) },
|
||
{ BRDATA (FLAG, chan_flag, 8, CHF_N_FLG, NUM_CHAN) },
|
||
{ NULL } };
|
||
|
||
MTAB chan_mod[] = {
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_W, "W", NULL,
|
||
NULL, &chan_show_reg, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_Y, "Y", NULL,
|
||
NULL, &chan_show_reg, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_C, "C", NULL,
|
||
NULL, &chan_show_reg, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_D, "D", NULL,
|
||
NULL, &chan_show_reg, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_E, "E", NULL,
|
||
NULL, &chan_show_reg, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_F, "F", NULL,
|
||
NULL, &chan_show_reg, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_G, "G", NULL,
|
||
NULL, &chan_show_reg, NULL },
|
||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_H, "H", NULL,
|
||
NULL, &chan_show_reg, NULL } };
|
||
|
||
DEVICE chan_dev = {
|
||
"CHAN", &chan_unit, chan_reg, chan_mod,
|
||
1, 8, 8, 1, 8, 8,
|
||
NULL, NULL, &chan_reset,
|
||
NULL, NULL, NULL };
|
||
|
||
/* Tables */
|
||
|
||
static const uint32 int_zc[8] = {
|
||
INT_WZWC, INT_YZWC, INT_CZWC, INT_DZWC,
|
||
INT_EZWC, INT_FZWC, INT_GZWC, INT_HZWC };
|
||
|
||
static const uint32 int_er[8] = {
|
||
INT_WEOR, INT_YEOR, INT_CEOR, INT_DEOR,
|
||
INT_EEOR, INT_FEOR, INT_GEOR, INT_HEOR };
|
||
|
||
/* dev_map maps device and channel numbers to a transfer flag masks */
|
||
|
||
uint32 dev_map[64][NUM_CHAN];
|
||
|
||
/* dev_dsp maps device and channel numbers to dispatch routines */
|
||
|
||
t_stat (*dev_dsp[64][NUM_CHAN])() = { NULL };
|
||
|
||
/* dev_dsp maps system device numbers to dispatch routines */
|
||
|
||
t_stat (*dev3_dsp[64])() = { NULL };
|
||
|
||
/* dev_alt maps alert numbers to dispatch routines */
|
||
|
||
struct aldisp dev_alt[] = {
|
||
{ NULL, NULL },
|
||
{ NULL, &pot_ilc }, { NULL, &pot_ilc },
|
||
{ NULL, &pot_ilc }, { NULL, &pot_ilc },
|
||
{ NULL, &pot_ilc }, { NULL, &pot_ilc },
|
||
{ NULL, &pot_ilc }, { NULL, &pot_ilc },
|
||
{ NULL, &pot_dcr }, { NULL, &pot_dcr },
|
||
{ NULL, &pot_dcr }, { NULL, &pot_dcr },
|
||
{ NULL, &pot_dcr }, { NULL, &pot_dcr },
|
||
{ NULL, &pot_dcr }, { NULL, &pot_dcr },
|
||
{ &pin_adr, NULL }, { &pin_adr, NULL },
|
||
{ &pin_adr, NULL }, { &pin_adr, NULL },
|
||
{ &pin_adr, NULL }, { &pin_adr, NULL },
|
||
{ &pin_adr, NULL }, { &pin_adr, NULL },
|
||
{ NULL, &pot_RL1 }, { NULL, &pot_RL2 },
|
||
{ NULL, &pot_RL4 },
|
||
{ &pin_rads, NULL }, { NULL, &pot_rada },
|
||
{ &pin_dsk, &pot_dsk }, { NULL, &pot_fork },
|
||
{ &pin_mux, &pot_mux } };
|
||
|
||
/* Single word I/O instructions */
|
||
|
||
t_stat op_wyim (uint32 inst, uint32 *dat)
|
||
{
|
||
int32 ch, dev;
|
||
|
||
ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */
|
||
dev = chan_uar[ch] & DEV_MASK; /* get dev # */
|
||
if (chan_cnt[ch] <= chan_cpw[ch]) { /* buffer empty? */
|
||
if (dev == 0) return STOP_INVIOP; /* no device? dead */
|
||
return STOP_IONRDY; } /* hang until full */
|
||
*dat = chan_war[ch]; /* get data */
|
||
chan_war[ch] = 0; /* reset war */
|
||
chan_cnt[ch] = 0; /* reset cnt */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat op_miwy (uint32 inst, uint32 dat)
|
||
{
|
||
int32 ch, dev;
|
||
|
||
ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */
|
||
dev = chan_uar[ch] & DEV_MASK; /* get dev # */
|
||
if (chan_cnt[ch] != 0) { /* buffer full? */
|
||
if (dev == 0) return STOP_INVIOP; /* no device? dead */
|
||
return STOP_IONRDY; } /* hang until full */
|
||
chan_war[ch] = dat; /* get data */
|
||
chan_cnt[ch] = chan_cpw[ch] + 1; /* buffer full */
|
||
if (chan_flag[ch] & CHF_OWAK) { /* output wake? */
|
||
if (VLD_DEV (dev, ch)) SET_XFR (dev, ch); /* wake channel */
|
||
chan_flag[ch] = chan_flag[ch] & ~CHF_OWAK; } /* clear wake */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat op_pin (uint32 *dat)
|
||
{
|
||
uint32 al = alert; /* local copy */
|
||
|
||
alert = 0; /* clear alert */
|
||
if ((al == 0) || (dev_alt[al].pin == NULL)) CRETIOP; /* inv alert? */
|
||
return dev_alt[al].pin (al, dat); /* PIN from dev */
|
||
}
|
||
|
||
t_stat op_pot (uint32 dat)
|
||
{
|
||
uint32 al = alert; /* local copy */
|
||
|
||
alert = 0; /* clear alert */
|
||
if ((al == 0) || (dev_alt[al].pot == NULL)) CRETIOP; /* inv alert? */
|
||
return dev_alt[al].pot (al, &dat); /* POT to dev */
|
||
}
|
||
|
||
/* EOM/EOD */
|
||
|
||
t_stat op_eomd (uint32 inst)
|
||
{
|
||
uint32 mod = I_GETIOMD (inst); /* get mode */
|
||
uint32 ch = I_GETEOCH (inst); /* get chan # */
|
||
uint32 dev = inst & DEV_MASK; /* get dev # */
|
||
uint32 ch_dev = chan_uar[ch] & DEV_MASK; /* chan curr dev # */
|
||
t_stat r;
|
||
|
||
switch (mod) {
|
||
case 0: /* IO control */
|
||
if (dev) { /* new dev? */
|
||
if (ch_dev) CRETIOP; /* chan act? err */
|
||
if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */
|
||
chan_war[ch] = chan_cnt[ch] = 0; /* init chan */
|
||
chan_flag[ch] = chan_dcr[ch] = 0;
|
||
chan_mode[ch] = chan_uar[ch] = 0;
|
||
if (ch > CHAN_E) chan_mode[ch] = CHM_CE;
|
||
if (r = dev_dsp[dev][ch] (IO_CONN, inst, NULL)) /* connect */
|
||
return r;
|
||
if ((inst & I_IND) || (ch >= CHAN_C)) { /* C-H? alert ilc */
|
||
alert = POT_ILCY + ch;
|
||
chan_mar[ch] = chan_wcr[ch] = 0; }
|
||
if (chan_flag[ch] & CHF_24B) chan_cpw[ch] = 0; /* 24B? 1 ch/wd */
|
||
else if (chan_flag[ch] & CHF_12B) /* 12B? 2 ch/wd */
|
||
chan_cpw[ch] = CHC_GETCPW (inst) & 1;
|
||
else chan_cpw[ch] = CHC_GETCPW (inst); /* 6b, 1-4 ch/wd */
|
||
chan_uar[ch] = dev; /* connected */
|
||
if ((dev & DEV_OUT) && ion && !QAILCE (alert)) /* out, prog IO? */
|
||
int_req = int_req | int_zc[ch]; } /* initial intr */
|
||
else return dev_disc (ch, ch_dev); /* disconnect */
|
||
break;
|
||
case 1: /* buf control */
|
||
if (QAILCE (alert)) { /* ilce alerted? */
|
||
ch = alert - POT_ILCY; /* derive chan */
|
||
if (ch >= CHAN_E) inst = inst | CHM_CE; /* DACC? ext */
|
||
chan_mode[ch] = inst; /* save mode */
|
||
chan_mar[ch] = (CHM_GETHMA (inst) << 14) | /* get hi mar */
|
||
(chan_mar[ch] & CHI_M_MA);
|
||
chan_wcr[ch] = (CHM_GETHWC (inst) << 10) | /* get hi wc */
|
||
(chan_wcr[ch] & CHI_M_WC); }
|
||
else if (dev) { /* dev EOM */
|
||
if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */
|
||
return dev_dsp[dev][ch] (IO_EOM1, inst, NULL); }
|
||
else { /* chan EOM */
|
||
inst = inst & 047677;
|
||
if (inst == 040000) { /* alert ilce */
|
||
alert = POT_ILCY + ch;
|
||
chan_mar[ch] = chan_wcr[ch] = 0; }
|
||
else if (inst == 002000) alert = POT_ADRY + ch; /* alert addr */
|
||
else if (inst == 001000) alert = POT_DCRY + ch; /* alert DCR */
|
||
else if (inst == 004000) { /* term output */
|
||
if (ch_dev & DEV_OUT) { /* to output dev? */
|
||
if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* busy, DMA in prog? */
|
||
chan_flag[ch] = chan_flag[ch] | CHF_TOP; /* TOP pending */
|
||
else return dev_wreor (ch, ch_dev); /* idle, write EOR */
|
||
} /* end else TOP */
|
||
else if (ch_dev & DEV_MT) { /* change to scan? */
|
||
chan_uar[ch] = chan_uar[ch] | DEV_MTS; /* change dev addr */
|
||
chan_flag[ch] = chan_flag[ch] | CHF_SCAN; /* set scan flag */
|
||
} /* end else change scan */
|
||
} /* end else term output */
|
||
} /* end else chan EOM */
|
||
break;
|
||
case 2: /* internal */
|
||
if (ch >= CHAN_E) { /* EOD? */
|
||
if (inst & 00300) { /* set EM? */
|
||
if (inst & 00100) EM2 = inst & 07;
|
||
if (inst & 00200) EM3 = (inst >> 3) & 07;
|
||
set_dyn_map (); }
|
||
break; } /* end if EOD */
|
||
if (inst & 00001) OV = 0; /* clr OV */
|
||
if (inst & 00002) ion = 1; /* ion */
|
||
else if (inst & 00004) ion = 0; /* iof */
|
||
if ((inst & 00010) && (((X >> 1) ^ X) & EXPS)) OV = 1;
|
||
if (inst & 00020) alert = POT_SYSI; /* alert sys int */
|
||
if (inst & 00100) rtc_pie = 1; /* arm clk pls */
|
||
else if (inst & 00200) rtc_pie = 0; /* disarm pls */
|
||
if ((inst & 01400) == 01400) alert = POT_RL4; /* alert RL4 */
|
||
else if (inst & 00400) alert = POT_RL1; /* alert RL1 */
|
||
else if (inst & 01000) alert = POT_RL2; /* alert RL2 */
|
||
if (inst & 02000) { /* nml to mon */
|
||
nml_mode = usr_mode = 0;
|
||
if (inst & 00400) mon_usr_trap = 1; }
|
||
break;
|
||
case 3: /* special */
|
||
dev = I_GETDEV3 (inst); /* special device */
|
||
if (dev3_dsp[dev]) /* defined? */
|
||
return dev3_dsp[dev] (IO_CONN, inst, NULL);
|
||
CRETINS; } /* end case */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Skip if not signal */
|
||
|
||
t_stat op_sks (uint32 inst, uint32 *dat)
|
||
{
|
||
uint32 mod = I_GETIOMD (inst); /* get mode */
|
||
uint32 ch = I_GETSKCH (inst); /* get chan # */
|
||
uint32 dev = inst & DEV_MASK; /* get dev # */
|
||
|
||
*dat = 0;
|
||
if ((ch == 4) && !(inst & 037774)) { /* EM test */
|
||
if (((inst & 0001) && (EM2 != 2)) ||
|
||
((inst & 0002) && (EM3 != 3))) *dat = 1;
|
||
return SCPE_OK; }
|
||
switch (mod) {
|
||
case 1: /* ch, dev */
|
||
if (dev) { /* device */
|
||
if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */
|
||
dev_dsp[dev][ch] (IO_SKS, inst, dat); } /* do test */
|
||
else { /* channel */
|
||
if (((inst & 04000) && (chan_uar[ch] == 0)) ||
|
||
((inst & 02000) && (chan_wcr[ch] == 0)) ||
|
||
((inst & 01000) && ((chan_flag[ch] & CHF_ERR) == 0)) ||
|
||
((inst & 00400) && (chan_flag[ch] & CHF_IREC))) *dat = 1; }
|
||
break;
|
||
case 2: /* internal test */
|
||
if (inst & 0001) { /* test OV */
|
||
*dat = OV ^ 1; /* skip if off */
|
||
OV = 0; /* and reset */
|
||
break; }
|
||
if (((inst & 00002) && !ion) || /* ion, bpt test */
|
||
((inst & 00004) && ion) ||
|
||
((inst & 00010) && ((chan_flag[CHAN_W] & CHF_ERR) == 0)) ||
|
||
((inst & 00020) && ((chan_flag[CHAN_Y] & CHF_ERR) == 0)) ||
|
||
((inst & 00040) && ((bpt & 001) == 0)) ||
|
||
((inst & 00100) && ((bpt & 002) == 0)) ||
|
||
((inst & 00200) && ((bpt & 004) == 0)) ||
|
||
((inst & 00400) && ((bpt & 010) == 0)) ||
|
||
((inst & 01000) && (chan_uar[CHAN_W] == 0)) ||
|
||
((inst & 02000) && (chan_uar[CHAN_Y] == 0))) *dat = 1;
|
||
break;
|
||
case 3: /* special */
|
||
dev = I_GETDEV3 (inst); /* special device */
|
||
if (dev3_dsp[dev]) dev3_dsp[dev] (IO_SKS, inst, dat);
|
||
else CRETINS; } /* end case */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* PIN/POT routines */
|
||
|
||
t_stat pot_ilc (uint32 num, uint32 *dat)
|
||
{
|
||
uint32 ch = num - POT_ILCY;
|
||
|
||
chan_mar[ch] = (chan_mar[ch] & ~CHI_M_MA) | CHI_GETMA (*dat);
|
||
chan_wcr[ch] = (chan_wcr[ch] & ~CHI_M_WC) | CHI_GETWC (*dat);
|
||
chan_flag[ch] = chan_flag[ch] | CHF_ILCE;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat pot_dcr (uint32 num, uint32 *dat)
|
||
{
|
||
uint32 ch = num - POT_DCRY;
|
||
|
||
chan_dcr[ch] = (*dat) & (CHD_INT | CHD_PAGE);
|
||
chan_flag[ch] = chan_flag[ch] | CHF_DCHN;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat pin_adr (uint32 num, uint32 *dat)
|
||
{
|
||
uint32 ch = num - POT_ADRY;
|
||
|
||
*dat = chan_mar[ch] & PAMASK;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* System interrupt POT.
|
||
|
||
The SDS 940 timesharing system uses a permanently asserted
|
||
system interrupt as a way of forking the teletype input
|
||
interrupt handler to a lower priority. The interrupt is
|
||
armed to set up the fork, and disarmed in the fork routine */
|
||
|
||
t_stat pot_fork (uint32 num, uint32 *dat)
|
||
{
|
||
uint32 igrp = SYI_GETGRP (*dat); /* get group */
|
||
uint32 fbit = (1 << (VEC_FORK & 017)); /* bit in group */
|
||
|
||
if (igrp == (VEC_FORK / 020)) { /* right group? */
|
||
if ((*dat & SYI_ARM) && (*dat & fbit)) /* arm, bit set? */
|
||
int_req = int_req | INT_FORK;
|
||
if ((*dat & SYI_DIS) && !(*dat & fbit)) /* disarm, bit clr? */
|
||
int_req = int_req & ~INT_FORK; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Channel read invokes the I/O device to get the next character and,
|
||
if not end of record, assembles it into the word assembly register.
|
||
If the interlace is on, the full word is stored in memory.
|
||
The key difference points for the various terminal functions are
|
||
|
||
end of record comp: EOT interrupt
|
||
IORD, IOSD: EOR interrupt, disconnect
|
||
IORP, IOSP: EOR interrupt, interrecord
|
||
interlace off: comp: EOW interrupt
|
||
IORD, IORP: ignore
|
||
IOSD, IOSP: overrun error
|
||
--wcr == 0: comp: clear interlace
|
||
IORD, IORP, IOSP: ZWC interrupt
|
||
IOSD: ZWC interrupt, EOR interrupt, disconnect
|
||
|
||
Note that the channel can be disconnected if CHN_EOR is set, but must
|
||
not be if XFR_REQ is set */
|
||
|
||
t_stat chan_read (int32 ch)
|
||
{
|
||
uint32 dat = 0;
|
||
uint32 dev = chan_uar[ch] & DEV_MASK;
|
||
uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
|
||
t_stat r = SCPE_OK;
|
||
|
||
if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */
|
||
if (INV_DEV (dev, ch)) CRETIOP; /* can't read? */
|
||
r = dev_dsp[dev][ch] (IO_READ, dev, &dat); /* read data */
|
||
if (r) chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* error? */
|
||
if (chan_flag[ch] & CHF_24B) chan_war[ch] = dat; /* 24B? */
|
||
else if (chan_flag[ch] & CHF_12B) /* 12B? */
|
||
chan_war[ch] = ((chan_war[ch] << 12) | (dat & 07777)) & DMASK;
|
||
else chan_war[ch] = ((chan_war[ch] << 6) | (dat & 077)) & DMASK;
|
||
if (chan_flag[ch] & CHF_SCAN) /* scanning? */
|
||
chan_cnt[ch] = chan_cpw[ch]; /* never full */
|
||
else chan_cnt[ch] = chan_cnt[ch] + 1; /* insert char */
|
||
if (chan_cnt[ch] > chan_cpw[ch]) { /* full? */
|
||
if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */
|
||
chan_write_mem (ch); /* write to mem */
|
||
if (chan_wcr[ch] == 0) { /* wc zero? */
|
||
chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* clr interlace */
|
||
if ((tfnc != CHM_COMP) && (chan_mode[ch] & CHM_ZC))
|
||
int_req = int_req | int_zc[ch]; /* zwc interrupt */
|
||
if (tfnc == CHM_IOSD) { /* IOSD? also EOR */
|
||
if (chan_mode[ch] & CHM_ER) int_req = int_req | int_er[ch];
|
||
dev_disc (ch, dev); /* disconnect */
|
||
} /* end if IOSD */
|
||
} /* end if wcr == 0 */
|
||
} /* end if ilce on */
|
||
else { /* interlace off */
|
||
if (TST_EOR (ch)) return chan_eor (ch); /* eor? */
|
||
if (tfnc == CHM_COMP) { /* C: EOW, intr */
|
||
if (ion) int_req = int_req | int_zc[ch]; }
|
||
else if (tfnc & CHM_SGNL) /* Sx: error */
|
||
chan_flag[ch] = chan_flag[ch] | CHF_ERR;
|
||
else chan_cnt[ch] = chan_cpw[ch]; /* Rx: ignore */
|
||
} /* end else ilce */
|
||
} /* end if full */
|
||
} /* end if xfr */
|
||
if (TST_EOR (ch)) { /* end record? */
|
||
if (tfnc == CHM_COMP) chan_flush_war (ch); /* C: fill war */
|
||
else if (chan_cnt[ch]) { /* RX, CX: fill? */
|
||
chan_flush_war (ch); /* fill war */
|
||
if (chan_flag[ch] & CHF_ILCE) /* ilce on? store */
|
||
chan_write_mem (ch); } /* end else if cnt */
|
||
return chan_eor (ch); } /* eot/eor int */
|
||
return r;
|
||
}
|
||
|
||
void chan_write_mem (int32 ch)
|
||
{
|
||
WriteP (chan_mar[ch], chan_war[ch]); /* write to mem */
|
||
chan_mar[ch] = chan_mar_inc (ch); /* incr mar */
|
||
chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr wcr */
|
||
chan_war[ch] = 0; /* reset war */
|
||
chan_cnt[ch] = 0; /* reset cnt */
|
||
return;
|
||
}
|
||
|
||
void chan_flush_war (int32 ch)
|
||
{
|
||
int32 i = (chan_cpw[ch] - chan_cnt[ch]) + 1;
|
||
|
||
if (i) {
|
||
if (chan_flag[ch] & CHF_24B) chan_war[ch] = 0;
|
||
else if (chan_flag[ch] & CHF_12B)
|
||
chan_war[ch] = (chan_war[ch] << 12) & DMASK;
|
||
else chan_war[ch] = (chan_war[ch] << (i * 6)) & DMASK;
|
||
chan_cnt[ch] = chan_cpw[ch] + 1; }
|
||
return;
|
||
}
|
||
|
||
/* Channel write gets the next character and sends it to the I/O device.
|
||
If this is the last character in an interlace operation, the end of
|
||
record operation is invoked.
|
||
The key difference points for the various terminal functions are
|
||
|
||
end of record: comp: EOT interrupt
|
||
IORD, IOSD: EOR interrupt, disconnect
|
||
IORP, IOSP: EOR interrupt, interrecord
|
||
interlace off: if not end of record, EOW interrupt
|
||
--wcr == 0: comp: EOT interrupt, disconnect
|
||
IORD, IORP: ignore
|
||
IOSD: ZWC interrupt, disconnect
|
||
IOSP: ZWC interrupt, interrecord
|
||
*/
|
||
t_stat chan_write (int32 ch)
|
||
{
|
||
uint32 dat = 0;
|
||
uint32 dev = chan_uar[ch] & DEV_MASK;
|
||
uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
|
||
t_stat r = SCPE_OK;
|
||
|
||
if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */
|
||
if (INV_DEV (dev, ch)) CRETIOP; /* invalid dev? */
|
||
if (chan_cnt[ch] == 0) { /* buffer empty? */
|
||
if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */
|
||
chan_war[ch] = ReadP (chan_mar[ch]);
|
||
chan_mar[ch] = chan_mar_inc (ch); /* incr mar */
|
||
chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr mar */
|
||
chan_cnt[ch] = chan_cpw[ch] + 1; } /* set cnt */
|
||
else { /* ilce off */
|
||
CLR_XFR (dev, ch); /* cant xfr */
|
||
if (TST_EOR (dev)) return chan_eor (ch); /* EOR? */
|
||
chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* rate err */
|
||
return SCPE_OK; } /* end else ilce */
|
||
} /* end if cnt */
|
||
chan_cnt[ch] = chan_cnt[ch] - 1; /* decr cnt */
|
||
if (chan_flag[ch] & CHF_24B) dat = chan_war[ch]; /* 24B? */
|
||
else if (chan_flag[ch] & CHF_12B) { /* 12B? */
|
||
dat = (chan_war[ch] >> 12) & 07777; /* get halfword */
|
||
chan_war[ch] = (chan_war[ch] << 12) & DMASK; } /* remove from war */
|
||
else { /* 6B */
|
||
dat = (chan_war[ch] >> 18) & 077; /* get char */
|
||
chan_war[ch] = (chan_war[ch] << 6) & DMASK; } /* remove from war */
|
||
r = dev_dsp[dev][ch] (IO_WRITE, dev, &dat); /* write */
|
||
if (r) chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* error? */
|
||
if (chan_cnt[ch] == 0) { /* buf empty? */
|
||
if (chan_flag[ch] & CHF_ILCE) { /* ilce on? */
|
||
if (chan_wcr[ch] == 0) { /* wc now 0? */
|
||
chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* ilc off */
|
||
if (tfnc == CHM_COMP) { /* compatible? */
|
||
if (ion) int_req = int_req | int_zc[ch];
|
||
dev_disc (ch, dev); /* disconnnect */
|
||
} /* end if comp */
|
||
else { /* extended */
|
||
if (chan_mode[ch] & CHM_ZC) /* ZWC int */
|
||
int_req = int_req | int_zc[ch];
|
||
if (tfnc == CHM_IOSD) { /* SD */
|
||
if (chan_mode[ch] & CHM_ER) /* EOR int */
|
||
int_req = int_req | int_er[ch];
|
||
dev_disc (ch, dev); /* disconnnect */
|
||
} /* end if SD */
|
||
else if (!(tfnc && CHM_SGNL) || /* IORx or IOSP TOP? */
|
||
(chan_flag[ch] & CHF_TOP))
|
||
dev_wreor (ch, dev); /* R: write EOR */
|
||
chan_flag[ch] = chan_flag[ch] & ~CHF_TOP;
|
||
} /* end else comp */
|
||
} /* end if wcr */
|
||
} /* end if ilce */
|
||
else if (chan_flag[ch] & CHF_TOP) { /* off, TOP pending? */
|
||
chan_flag[ch] = chan_flag[ch] & ~CHF_TOP; /* clear TOP */
|
||
dev_wreor (ch, dev); } /* write EOR */
|
||
else if (ion) int_req = int_req | int_zc[ch]; /* no TOP, EOW intr */
|
||
} /* end if cnt */
|
||
} /* end if xfr */
|
||
if (TST_EOR (ch)) return chan_eor (ch); /* eor rcvd? */
|
||
return r;
|
||
}
|
||
|
||
/* MAR increment */
|
||
|
||
uint32 chan_mar_inc (int32 ch)
|
||
{
|
||
uint32 t = (chan_mar[ch] + 1) & PAMASK; /* incr mar */
|
||
|
||
if ((chan_flag[ch] & CHF_DCHN) && ((t & VA_POFF) == 0)) { /* chain? */
|
||
chan_flag[ch] = chan_flag[ch] & ~CHF_DCHN; /* clr flag */
|
||
if (chan_dcr[ch] & CHD_INT) /* if armed, intr */
|
||
int_req = int_req | int_zc[ch];
|
||
t = (chan_dcr[ch] & CHD_PAGE) << VA_V_PN; } /* new mar */
|
||
return t;
|
||
}
|
||
|
||
/* End of record action */
|
||
|
||
t_stat chan_eor (int32 ch)
|
||
{
|
||
uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
|
||
uint32 dev = chan_uar[ch] & DEV_MASK;
|
||
|
||
chan_flag[ch] = chan_flag[ch] & ~(CHF_EOR | CHF_ILCE); /* clr eor, ilce */
|
||
if (((tfnc == CHM_COMP) && ion) || (chan_mode[ch] & CHM_ER))
|
||
int_req = int_req | int_er[ch]; /* EOT/EOR? */
|
||
if (dev && (tfnc & CHM_PROC)) /* P, still conn? */
|
||
chan_flag[ch] = chan_flag[ch] | CHF_IREC; /* interrecord */
|
||
else return dev_disc (ch, dev); /* disconnect */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Utility routines */
|
||
|
||
t_stat dev_disc (uint32 ch, uint32 dev)
|
||
{
|
||
chan_uar[ch] = 0; /* disconnect */
|
||
if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_DISC, dev, NULL);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat dev_wreor (uint32 ch, uint32 dev)
|
||
{
|
||
if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_WREOR, dev, NULL);
|
||
chan_flag[ch] = chan_flag[ch] | CHF_EOR; /* set eor */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Externally visible routines */
|
||
/* Channel driver */
|
||
|
||
t_stat chan_process (void)
|
||
{
|
||
int32 i, dev;
|
||
t_stat r;
|
||
|
||
for (i = 0; i < NUM_CHAN; i++) { /* loop thru */
|
||
dev = chan_uar[i] & DEV_MASK; /* get dev */
|
||
if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) { /* chan active? */
|
||
if (dev & DEV_OUT) r = chan_write (i); /* write */
|
||
else r = chan_read (i); /* read */
|
||
if (r) return r; } }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Test for channel active */
|
||
|
||
t_bool chan_testact (void)
|
||
{
|
||
int32 i, dev;
|
||
|
||
for (i = 0; i < NUM_CHAN; i++) {
|
||
dev = chan_uar[i] & DEV_MASK;
|
||
if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) return 1; }
|
||
return 0;
|
||
}
|
||
|
||
/* Async output device ready for more data */
|
||
|
||
void chan_set_ordy (int32 ch)
|
||
{
|
||
if ((ch >= 0) && (ch < NUM_CHAN)) {
|
||
int32 dev = chan_uar[ch] & DEV_MASK; /* get dev */
|
||
if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* buf or ilce? */
|
||
SET_XFR (dev, ch); /* set xfr flg */
|
||
else chan_flag[ch] = chan_flag[ch] | CHF_OWAK; } /* need wakeup */
|
||
return;
|
||
}
|
||
|
||
/* Set flag in channel */
|
||
|
||
void chan_set_flag (int32 ch, uint32 fl)
|
||
{
|
||
if ((ch >= 0) && (ch < NUM_CHAN)) chan_flag[ch] = chan_flag[ch] | fl;
|
||
return;
|
||
}
|
||
|
||
/* Set UAR in channel */
|
||
|
||
void chan_set_uar (int32 ch, uint32 dev)
|
||
{
|
||
if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = dev & DEV_MASK;
|
||
return;
|
||
}
|
||
|
||
/* Disconnect channel */
|
||
|
||
void chan_disc (int32 ch)
|
||
{
|
||
if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = 0;
|
||
return;
|
||
}
|
||
|
||
/* Reset channels */
|
||
|
||
t_stat chan_reset (DEVICE *dptr)
|
||
{
|
||
int32 i;
|
||
|
||
xfr_req = 0;
|
||
for (i = 0; i < NUM_CHAN; i++) {
|
||
chan_uar[i] = 0;
|
||
chan_wcr[i] = 0;
|
||
chan_mar[i] = 0;
|
||
chan_dcr[i] = 0;
|
||
chan_war[i] = 0;
|
||
chan_cpw[i] = 0;
|
||
chan_cnt[i] = 0;
|
||
chan_mode[i] = 0;
|
||
chan_flag[i] = 0; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Channel assignment routines */
|
||
|
||
t_stat set_chan (UNIT *uptr, int32 val, char *sptr, void *desc)
|
||
{
|
||
DEVICE *dptr;
|
||
DIB *dibp;
|
||
int32 i;
|
||
|
||
if (sptr == NULL) return SCPE_ARG; /* valid args? */
|
||
if (uptr == NULL) return SCPE_IERR;
|
||
dptr = find_dev_from_unit (uptr);
|
||
if (dptr == NULL) return SCPE_IERR;
|
||
dibp = (DIB *) dptr->ctxt;
|
||
if (dibp == NULL) return SCPE_IERR;
|
||
for (i = 0; i < NUM_CHAN; i++) { /* match input */
|
||
if (strcmp (sptr, chname[i]) == 0) { /* find string */
|
||
if (val && !(val & (1 << i))) return SCPE_ARG; /* legal? */
|
||
dibp->chan = i; /* store new */
|
||
return SCPE_OK; } }
|
||
return SCPE_ARG;
|
||
}
|
||
|
||
t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||
{
|
||
DEVICE *dptr;
|
||
DIB *dibp;
|
||
|
||
if (uptr == NULL) return SCPE_IERR;
|
||
dptr = find_dev_from_unit (uptr);
|
||
if (dptr == NULL) return SCPE_IERR;
|
||
dibp = (DIB *) dptr->ctxt;
|
||
if (dibp == NULL) return SCPE_IERR;
|
||
fprintf (st, "channel=%s", chname[dibp->chan]);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Init device tables */
|
||
|
||
t_bool io_init (void)
|
||
{
|
||
DEVICE *dptr;
|
||
DIB *dibp;
|
||
DSPT *tplp;
|
||
int32 ch;
|
||
uint32 i, j, dev, doff;
|
||
|
||
/* Clear dispatch table, device map */
|
||
|
||
for (i = 0; i < NUM_CHAN; i++) {
|
||
for (j = 0; j < (DEV_MASK + 1); j++) {
|
||
dev_dsp[j][i] = NULL;
|
||
dev_map[j][i] = 0; } }
|
||
|
||
/* Test each device for conflict; add to map; init tables */
|
||
|
||
for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */
|
||
dibp = (DIB *) dptr->ctxt; /* get DIB */
|
||
if ((dibp == NULL) || (dptr->flags & DEV_DIS)) continue; /* exist, enabled? */
|
||
ch = dibp->chan; /* get channel */
|
||
dev = dibp->dev; /* get device num */
|
||
if (ch < 0) dev3_dsp[dev] = dibp->iop; /* special device */
|
||
else {
|
||
if (dibp->tplt == NULL) return TRUE; /* must have template */
|
||
for (tplp = dibp->tplt; tplp->num; tplp++) { /* loop thru templates */
|
||
for (j = 0; j < tplp->num; j++) { /* repeat as needed */
|
||
doff = dev + tplp->off + j; /* get offset dnum */
|
||
if (dev_map[doff][ch]) { /* slot in use? */
|
||
printf ("Device number conflict, chan = %s, devno = %02o\n",
|
||
chname[ch], doff);
|
||
if (sim_log) fprintf (sim_log,
|
||
"Device number conflict, chan = %s, dev = %02o\n",
|
||
chname[ch], doff);
|
||
return TRUE; }
|
||
dev_map[doff][ch] = dibp->xfr; /* set xfr flag */
|
||
dev_dsp[doff][ch] = dibp->iop; /* set dispatch */
|
||
} /* end for j */
|
||
} /* end for tplt */
|
||
} /* end else */
|
||
} /* end for i */
|
||
return FALSE;
|
||
}
|
||
|
||
/* Display channel state */
|
||
|
||
t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||
{
|
||
if ((val < 0) || (val >= NUM_CHAN)) return SCPE_IERR;
|
||
fprintf (st, "UAR: %02o\n", chan_uar[val]);
|
||
fprintf (st, "WCR: %05o\n", chan_wcr[val]);
|
||
fprintf (st, "MAR: %06o\n", chan_mar[val]);
|
||
fprintf (st, "DCR: %02o\n", chan_dcr[val]);
|
||
fprintf (st, "WAR: %08o\n", chan_war[val]);
|
||
fprintf (st, "CPW: %o\n", chan_cpw[val]);
|
||
fprintf (st, "CNT: %o\n", chan_cnt[val]);
|
||
fprintf (st, "MODE: %03o\n", chan_mode[val]);
|
||
fprintf (st, "FLAG: %04o\n", chan_flag[val]);
|
||
return SCPE_OK;
|
||
}
|