simh-testsetgenerator/SDS/sds_io.c
Bob Supnik 2bcd1e7c4c Notes For V2.10-2
1. New Features in 2.10-2

The build procedures have changed.  There is only one UNIX makefile.
To compile without Ethernet support, simply type

	gmake {target|all}

To compile with Ethernet support, type

	gmake USE_NETWORK=1 {target|all}

The Mingw batch files require Mingw release 2 and invoke the Unix
makefile.  There are still separate batch files for compilation
with or without Ethernet support.

1.1 SCP and Libraries

- The EVAL command will evaluate a symbolic type-in and display
  it in numeric form.
- The ! command (with no arguments) will launch the host operating
  system command shell.  The ! command (with an argument) executes
  the argument as a host operating system command.  (Code from
  Mark Pizzolato)
- Telnet sessions now recognize BREAK.  How a BREAK is transmitted
  dependent on the particular Telnet client.  (Code from Mark
  Pizzolato)
- The sockets library includes code for active connections as
  well as listening connections.
- The RESTORE command will restore saved memory size, if the
  simulator supports dynamic memory resizing.

1.2 PDP-1

- The PDP-1 supports the Type 24 serial drum (based on recently
  discovered documents).

1.3 18b PDP's

- The PDP-4 supports the Type 24 serial drum (based on recently
  discovered documents).

1.4 PDP-11

- The PDP-11 implements a stub DEUNA/DELUA (XU).  The real XU
  module will be included in a later release.

1.5 PDP-10

- The PDP-10 implements a stub DEUNA/DELUA (XU).  The real XU
  module will be included in a later release.

1.6 HP 2100

- The IOP microinstruction set is supported for the 21MX as well
  as the 2100.
- The HP2100 supports the Access Interprocessor Link (IPL).

1.7 VAX

- If the VAX console is attached to a Telnet session, BREAK is
  interpreted as console halt.
- The SET/SHOW HISTORY commands enable and display a history of
  the most recently executed instructions.  (Code from Mark
  Pizzolato)

1.8 Terminals Multiplexors

- BREAK detection was added to the HP, DEC, and Interdata terminal
  multiplexors.

1.9 Interdata 16b and 32b

- First release.  UNIX is not yet working.

1.10 SDS 940

- First release.

2. Bugs Fixed in 2.10-2

- PDP-11 console must default to 7b for early UNIX compatibility.
- PDP-11/VAX TMSCP emulator was using the wrong packet length for
  read/write end packets.
- Telnet IAC+IAC processing was fixed, both for input and output
  (found by Mark Pizzolato).
- PDP-11/VAX Ethernet setting flag bits wrong for chained
  descriptors (found by Mark Pizzolato).

3. New Features in 2.10 vs prior releases

3.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.

3.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.

3.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.

3.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.

3.5 PDP-1

- DECtape (then known as MicroTape) support has been added.
- The line printer and DECtape can be disabled and enabled.

3.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.

3.7 IBM 1620

- The IBM 1620 simulator has been released.

3.8 AltairZ80

- A hard drive has been added for increased storage.
- Several bugs have been fixed.

3.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.

3.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.

3.11 Simulated DECtapes

- Added support for RT11 image file format (256 x 16b) to DECtapes.

4. Bugs Fixed in 2.10 vs prior releases

- 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.
- PDP-10 tape wouldn't boot, and then wouldn't read (reported by
  Michael Thompson and Harris Newman, respectively)
- PDP-1 typewriter is half duplex, with only one shift state for
  both input and output (found by Derek Peschel)

5. General Notes

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.
2011-04-15 08:33:56 -07:00

886 lines
31 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* sds_io.c: SDS 940 I/O simulator
Copyright (c) 2001-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.
*/
#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;
}