980 lines
48 KiB
C
980 lines
48 KiB
C
/* hp2100_tty.c: HP 12531C Buffered Teleprinter Interface simulator
|
|
|
|
Copyright (c) 1993-2016, Robert M. Supnik
|
|
Copyright (c) 2017-2018, J. David Bryan
|
|
|
|
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
|
|
AUTHORS 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 names of the authors 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 authors.
|
|
|
|
TTY 12531C Buffered Teleprinter Interface
|
|
|
|
22-Sep-18 JDB RESET -P now resets to original FASTTIME timing
|
|
29-Jun-18 JDB TTY may now be disabled; added "set_endis" routine
|
|
27-Jun-18 JDB Added REALTIME/FASTTIME modes and tracing
|
|
05-Jun-18 JDB Revised I/O model
|
|
04-Jun-18 JDB Split out from hp2100_stddev.c
|
|
Trimmed revisions to current file applicability
|
|
22-Nov-17 JDB Fixed TTY serial output buffer overflow handling
|
|
03-Aug-17 JDB PTP and TTY now append to existing file data
|
|
01-May-17 JDB Deleted ttp_stopioe, as a detached punch is no longer an error
|
|
17-Jan-17 JDB Changed "hp_---sc" and "hp_---dev" to "hp_---_dib"
|
|
30-Dec-16 JDB Modified the TTY to print if the punch is not attached
|
|
13-May-16 JDB Modified for revised SCP API function parameter types
|
|
24-Dec-14 JDB Added casts for explicit downward conversions
|
|
18-Dec-12 MP Now calls sim_activate_time to get remaining poll time
|
|
09-May-12 JDB Separated assignments from conditional expressions
|
|
10-Feb-12 JDB Deprecated DEVNO in favor of SC
|
|
28-Mar-11 JDB Tidied up signal handling
|
|
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
|
26-Jun-08 JDB Rewrote device I/O to model backplane signals
|
|
25-Apr-08 JDB Changed TTY output wait from 100 to 200 for MSU BASIC
|
|
18-Apr-08 JDB Removed redundant control char handling definitions
|
|
14-Apr-08 JDB Changed TTY console poll to 10 msec. real time
|
|
Added UNIT_IDLE to TTY and CLK
|
|
31-Dec-07 JDB Corrected and verified ioCRS actions
|
|
28-Dec-06 JDB Added ioCRS state to I/O decoders
|
|
22-Nov-05 RMS Revised for new terminal processing routines
|
|
15-Aug-04 RMS Added tab to control char set (from Dave Bryan)
|
|
14-Jul-04 RMS Generalized handling of control char echoing
|
|
(from Dave Bryan)
|
|
26-Apr-04 RMS Fixed SFS x,C and SFC x,C
|
|
Fixed input behavior during typeout for RTE-IV
|
|
Suppressed nulls on TTY output for RTE-IV
|
|
Implemented DMA SRQ (follows FLG)
|
|
29-Mar-03 RMS Added support for console backpressure
|
|
22-Dec-02 RMS Added break support
|
|
01-Nov-02 RMS Fixed bug in TTY reset, TTY starts in input mode
|
|
Fixed bug in TTY mode OTA, stores data as well
|
|
Added UC option to TTY output
|
|
30-May-02 RMS Widened POS to 32b
|
|
22-Mar-02 RMS Revised for dynamically allocated memory
|
|
03-Nov-01 RMS Changed DEVNO to use extended SET/SHOW
|
|
29-Nov-01 RMS Added read only unit support
|
|
07-Sep-01 RMS Moved function prototypes
|
|
21-Nov-00 RMS Fixed flag, buffer power up state
|
|
Added status input for ptp, tty
|
|
15-Oct-00 RMS Added dynamic device number support
|
|
|
|
References:
|
|
- 12531C Buffered Teleprinter Interface Kit Operating and Service Manual
|
|
(12531-90033, November 1972)
|
|
|
|
|
|
The 12531C Buffered Teleprinter Interface connects current-loop devices, such
|
|
as the HP 2752A (ASR33) and 2754A (ASR35) teleprinters, as well as EIA RS-232
|
|
devices, such as the HP 2749A (ASR33) teleprinter and HP 2600 terminal, to
|
|
the CPU. Teleprinters are often used as the system consoles for the various
|
|
HP operating systems. By default, the system console is connected to the
|
|
simulation console, so that SCP and operating system commands may be entered
|
|
from the same window.
|
|
|
|
The interface responds to I/O instructions as follows:
|
|
|
|
Output Data Word format (OTA and OTB):
|
|
|
|
15 |14 13 12 |11 10 9 | 8 7 6 | 5 4 3 | 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 0 | - - - - - - - | output character | data
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | I | P | N | - - - - - - - - - - - - | control
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
I = set the interface to output/input mode (0/1)
|
|
P = enable the printer for output
|
|
N = enable the punch for output
|
|
|
|
|
|
Input Data Word format (LIA and LIB):
|
|
|
|
15 |14 13 12 |11 10 9 | 8 7 6 | 5 4 3 | 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| B | - - - - - - - | input character |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
B = interface is idle/busy (0/1)
|
|
|
|
To support CPU idling, the teleprinter interface polls for input by
|
|
coscheduling with the system poll timer, a calibrated timer with a ten
|
|
millisecond period. Other polled-keyboard input devices (multiplexers and
|
|
the BACI card) synchronize with the poll timer to ensure maximum available
|
|
idle time.
|
|
*/
|
|
|
|
|
|
|
|
#include "hp2100_defs.h"
|
|
#include "hp2100_io.h"
|
|
|
|
|
|
|
|
/* TTY program constants */
|
|
|
|
#define NUL '\000' /* null */
|
|
#define CR '\r' /* carriage return */
|
|
#define LF '\n' /* line feed */
|
|
#define MARK '\377' /* all bits marking */
|
|
|
|
|
|
/* TTY device flags */
|
|
|
|
#define DEV_REALTIME_SHIFT (DEV_V_UF + 0) /* realistic timing mode */
|
|
|
|
#define DEV_REALTIME (1u << DEV_REALTIME_SHIFT) /* realistic timing mode flag */
|
|
|
|
|
|
/* TTY unit flags */
|
|
|
|
#define UNIT_AUTOLF_SHIFT (TTUF_V_UF + 0) /* automatic line feed mode */
|
|
|
|
#define UNIT_AUTOLF (1u << UNIT_AUTOLF_SHIFT) /* automatic line feed mode flag */
|
|
|
|
|
|
/* TTY unit references */
|
|
|
|
typedef enum {
|
|
keyboard, /* teleprinter keyboard unit index */
|
|
printer, /* teleprinter printer unit index */
|
|
punch /* teleprinter punch unit index */
|
|
} UNIT_INDEX;
|
|
|
|
|
|
/* TTY device properties */
|
|
|
|
#define TTY_FAST_TIME 200 /* teleprinter optimized timing delay */
|
|
#define TTY_REAL_TIME mS (100) /* teleprinter realistic timing delay */
|
|
|
|
|
|
/* TTY output data and control words.
|
|
|
|
15 |14 13 12 |11 10 9 | 8 7 6 | 5 4 3 | 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 0 | - - - - - - - | output character | data
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | I | P | N | - - - - - - - - - - - - | control
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
*/
|
|
|
|
#define CN_CONTROL 0100000u /* output is a data/control (0/1) word */
|
|
#define CN_INPUT 0040000u /* set the interface to output/input (0/1) mode */
|
|
#define CN_PRINT 0020000u /* enable the printer for output */
|
|
#define CN_PUNCH 0010000u /* enable the punch for output */
|
|
|
|
static const BITSET_NAME tty_control_names [] = { /* Teleprinter control word names */
|
|
"\1input\0output", /* bit 14 */
|
|
"printer enabled", /* bit 13 */
|
|
"punch enabled" /* bit 12 */
|
|
};
|
|
|
|
static const BITSET_FORMAT tty_control_format = /* names, offset, direction, alternates, bar */
|
|
{ FMT_INIT (tty_control_names, 12, msb_first, has_alt, no_bar) };
|
|
|
|
|
|
/* TTY input data word.
|
|
|
|
15 |14 13 12 |11 10 9 | 8 7 6 | 5 4 3 | 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| B | - - - - - - - | input character |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
*/
|
|
|
|
#define ST_BUSY 0100000u /* interface is idle/busy (0/1) */
|
|
|
|
static const BITSET_NAME tty_status_names [] = { /* Teleprinter status word names */
|
|
"\1busy\0idle" /* bit 15 */
|
|
};
|
|
|
|
static const BITSET_FORMAT tty_status_format = /* names, offset, direction, alternates, bar */
|
|
{ FMT_INIT (tty_status_names, 15, msb_first, has_alt, no_bar) };
|
|
|
|
|
|
/* TTY local state declarations */
|
|
|
|
typedef struct {
|
|
char io_data; /* input/output data register */
|
|
char shift_in_data; /* shift-in data */
|
|
t_bool cr_seen; /* carriage return has been seen on input */
|
|
HP_WORD mode; /* control mode register */
|
|
|
|
FLIP_FLOP control; /* control flip-flop */
|
|
FLIP_FLOP flag; /* flag flip-flop */
|
|
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
|
|
} CARD_STATE;
|
|
|
|
static CARD_STATE tty; /* per-card state */
|
|
|
|
static int32 fast_data_time = TTY_FAST_TIME; /* fast receive/send time */
|
|
|
|
|
|
/* TTY I/O interface routine declarations */
|
|
|
|
static INTERFACE tty_interface;
|
|
|
|
|
|
/* TTY local SCP support routine declarations */
|
|
|
|
static t_stat set_filter (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
|
|
static t_stat set_auto (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
|
|
static t_stat set_mode (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
|
|
static t_stat set_endis (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
|
|
|
|
static t_stat show_mode (FILE *st, UNIT *uptr, int32 value, CONST void *desc);
|
|
|
|
static t_stat tty_reset (DEVICE *dptr);
|
|
|
|
|
|
/* TTY local utility routine declarations */
|
|
|
|
static t_stat keyboard_service (UNIT *uptr);
|
|
static t_stat print_punch_service (UNIT *uptr);
|
|
static t_stat output (int32 character);
|
|
|
|
|
|
/* TTY SCP data declarations */
|
|
|
|
/* Unit lists.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The print unit service handles both printing and punching. The punch
|
|
unit is never scheduled, so its event routine is NULL.
|
|
*/
|
|
|
|
static UNIT tty_unit [] = {
|
|
/* Event Routine Unit Flags Capacity Delay */
|
|
/* -------------------- ------------------------------------ -------- ------------- */
|
|
{ UDATA (&keyboard_service, TT_MODE_UC | UNIT_IDLE, 0), POLL_PERIOD },
|
|
{ UDATA (&print_punch_service, TT_MODE_UC, 0), TTY_FAST_TIME },
|
|
{ UDATA (NULL, TT_MODE_8B | UNIT_SEQ | UNIT_ATTABLE, 0), 0 }
|
|
};
|
|
|
|
#define key_unit tty_unit [keyboard] /* teleprinter keyboard unit */
|
|
#define print_unit tty_unit [printer] /* teleprinter printer unit */
|
|
#define punch_unit tty_unit [punch] /* teleprinter punch unit */
|
|
|
|
|
|
/* Device information block */
|
|
|
|
static DIB tty_dib = {
|
|
&tty_interface, /* the device's I/O interface function pointer */
|
|
TTY, /* the device's select code (02-77) */
|
|
0, /* the card index */
|
|
"12531C Buffered Teleprinter Interface", /* the card description */
|
|
NULL /* the ROM description */
|
|
};
|
|
|
|
|
|
/* Register list */
|
|
|
|
static REG tty_reg [] = {
|
|
/* Macro Name Location Radix Width Offset Depth Flags */
|
|
/* ------ ------ ------------------------ ----- ---------- ------ ------------- ----------------- */
|
|
{ ORDATA (BUF, tty.io_data, 8) },
|
|
{ GRDATA (MODE, tty.mode, 2, 3, 12), PV_RZRO },
|
|
{ ORDATA (SHIN, tty.shift_in_data, 8), REG_HRO },
|
|
{ FLDATA (CTL, tty.control, 0) },
|
|
{ FLDATA (FLG, tty.flag, 0) },
|
|
{ FLDATA (FBF, tty.flag_buffer, 0) },
|
|
{ FLDATA (KLFP, tty.cr_seen, 0), REG_HRO },
|
|
{ DRDATA (KPOS, key_unit.pos, T_ADDR_W), PV_LEFT },
|
|
{ DRDATA (TPOS, print_unit.pos, T_ADDR_W), PV_LEFT },
|
|
{ DRDATA (PPOS, punch_unit.pos, T_ADDR_W), PV_LEFT },
|
|
{ DRDATA (TTIME, fast_data_time, 24), PV_LEFT | REG_NZ },
|
|
|
|
DIB_REGS (tty_dib),
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
|
|
/* Modifier list */
|
|
|
|
static MTAB tty_mod [] = {
|
|
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
|
|
/* ----------- ----------- --------------- ------------ ----------- ------- ---------- */
|
|
{ TT_MODE, TT_MODE_UC, "UC", "UC", &set_filter, NULL, NULL },
|
|
{ TT_MODE, TT_MODE_7B, "7b", "7B", &set_filter, NULL, NULL },
|
|
{ TT_MODE, TT_MODE_8B, "8b", "8B", &set_filter, NULL, NULL },
|
|
{ TT_MODE, TT_MODE_7P, "7p", "7P", &set_filter, NULL, NULL },
|
|
|
|
{ UNIT_AUTOLF, UNIT_AUTOLF, "auto linefeed", "AUTOLF", &set_auto, NULL, NULL },
|
|
{ UNIT_AUTOLF, 0, NULL, "NOAUTOLF", &set_auto, NULL, NULL },
|
|
|
|
/* Entry Flags Value Print String Match String Validation Display Descriptor */
|
|
/* ------------------- ------------ ------------ ------------ ------------ ------------ ----------------- */
|
|
{ MTAB_XDV, 0, NULL, "FASTTIME", &set_mode, NULL, NULL },
|
|
{ MTAB_XDV, DEV_REALTIME, NULL, "REALTIME", &set_mode, NULL, NULL },
|
|
{ MTAB_XDV, 0, "MODES", NULL, NULL, &show_mode, NULL },
|
|
|
|
{ MTAB_XDV | MTAB_NMO, 1, NULL, "ENABLED", &set_endis, NULL, NULL },
|
|
{ MTAB_XDV | MTAB_NMO, 0, NULL, "DISABLED", &set_endis, NULL, NULL },
|
|
|
|
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &tty_dib },
|
|
{ MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &tty_dib },
|
|
{ 0 }
|
|
};
|
|
|
|
|
|
/* Trace list */
|
|
|
|
static DEBTAB tty_deb [] = {
|
|
{ "CSRW", TRACE_CSRW }, /* trace interface control, status, read, and write actions */
|
|
{ "SERV", TRACE_SERV }, /* trace unit service scheduling calls and entries */
|
|
{ "PSERV", TRACE_PSERV }, /* trace periodic unit service scheduling calls and entries */
|
|
{ "XFER", TRACE_XFER }, /* trace data transmissions */
|
|
{ "IOBUS", TRACE_IOBUS }, /* trace I/O bus signals and data words received and returned */
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
|
|
/* Device descriptor */
|
|
|
|
DEVICE tty_dev = {
|
|
"TTY", /* device name */
|
|
tty_unit, /* unit array */
|
|
tty_reg, /* register array */
|
|
tty_mod, /* modifier array */
|
|
3, /* number of units */
|
|
10, /* address radix */
|
|
31, /* address width */
|
|
1, /* address increment */
|
|
8, /* data radix */
|
|
8, /* data width */
|
|
NULL, /* examine routine */
|
|
NULL, /* deposit routine */
|
|
&tty_reset, /* reset routine */
|
|
NULL, /* boot routine */
|
|
&hp_attach, /* attach routine */
|
|
NULL, /* detach routine */
|
|
&tty_dib, /* device information block pointer */
|
|
DEV_DISABLE | DEV_DEBUG, /* device flags */
|
|
0, /* debug control flags */
|
|
tty_deb, /* debug flag name array */
|
|
NULL, /* memory size change routine */
|
|
NULL /* logical device name */
|
|
};
|
|
|
|
|
|
|
|
/* TTY I/O interface routine */
|
|
|
|
|
|
/* Teleprinter interface.
|
|
|
|
The teleprinter interface is installed on the I/O bus and receives I/O
|
|
commands from the CPU and DMA/DCPC channels. In simulation, the asserted
|
|
signals on the bus are represented as bits in the inbound_signals set. Each
|
|
signal is processed sequentially in ascending numerical order.
|
|
|
|
The interface mode (output or input) determines whether STC transmits the
|
|
content of the output register or merely enables reception interrupts. While
|
|
in output mode, the input shift register is active and will reflect the state
|
|
of the serial input line while printing occurs. RTE uses this behavior to
|
|
detect a keypress during output on the system console and so to initiate
|
|
command input.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The print unit service time has to be set on each output request, even
|
|
though the time does not change unless a SET TTY REALTIME/FASTTIME
|
|
command is issued. We want to be able to respond immediately to a change
|
|
in the FASTTIME register setting via a DEPOSIT TTY TTIME <new-time>
|
|
command without requiring a subsequent SET TTY FASTTIME command to
|
|
recognize the change. However, there is no VM notification that the
|
|
value was changed unless all DEPOSIT commands are intercepted. The
|
|
straightforward solution of having SET change a pointer to the selected
|
|
value, i.e., to point at the REALTIME value or the FASTTIME value, and
|
|
then using that pointer indirectly to get the service time won't work
|
|
across a SAVE/RESTORE. The pointer itself cannot be saved, as the
|
|
restoration might be done on a different SIMH version, and there's no VM
|
|
notification that a RESTORE was done unless the device was attached when
|
|
the SAVE was done, so there's no way to set the pointer during a RESTORE.
|
|
*/
|
|
|
|
static SIGNALS_VALUE tty_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
|
|
{
|
|
INBOUND_SIGNAL signal;
|
|
INBOUND_SET working_set = inbound_signals;
|
|
SIGNALS_VALUE outbound = { ioNONE, 0 };
|
|
t_bool irq_enabled = FALSE;
|
|
|
|
while (working_set) { /* while signals remain */
|
|
signal = IONEXTSIG (working_set); /* isolate the next signal */
|
|
|
|
switch (signal) { /* dispatch the I/O signal */
|
|
|
|
case ioCLF: /* Clear Flag flip-flop */
|
|
tty.flag_buffer = CLEAR; /* reset the flag buffer */
|
|
tty.flag = CLEAR; /* and flag flip-flops */
|
|
break;
|
|
|
|
|
|
case ioSTF: /* Set Flag flip-flop */
|
|
tty.flag_buffer = SET; /* set the flag buffer flip-flop */
|
|
break;
|
|
|
|
|
|
case ioENF: /* Enable Flag */
|
|
if (tty.flag_buffer == SET) /* if the flag buffer flip-flop is set */
|
|
tty.flag = SET; /* then set the flag flip-flop */
|
|
break;
|
|
|
|
|
|
case ioSFC: /* Skip if Flag is Clear */
|
|
if (tty.flag == CLEAR) /* if the flag flip-flop is clear */
|
|
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
|
|
break;
|
|
|
|
|
|
case ioSFS: /* Skip if Flag is Set */
|
|
if (tty.flag == SET) /* if the flag flip-flop is set */
|
|
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
|
|
break;
|
|
|
|
|
|
case ioIOI: /* I/O Data Input */
|
|
outbound.value = TO_WORD (0, tty.io_data); /* get the data register value */
|
|
|
|
if ((tty.mode & CN_INPUT) == 0 /* if the card is in output mode */
|
|
&& sim_is_active (&print_unit)) /* and the printer is active */
|
|
outbound.value |= ST_BUSY; /* then set the busy status bit */
|
|
|
|
tprintf (tty_dev, TRACE_CSRW, "Status is %s | %s\n",
|
|
fmt_bitset (outbound.value, tty_status_format),
|
|
fmt_char (outbound.value));
|
|
break;
|
|
|
|
|
|
case ioIOO: /* I/O Data Output */
|
|
if (inbound_value & CN_CONTROL) { /* if this is a control word */
|
|
tty.mode = inbound_value; /* then set the mode register */
|
|
|
|
tprintf (tty_dev, TRACE_CSRW, "Control is %s\n",
|
|
fmt_bitset (inbound_value, tty_control_format));
|
|
}
|
|
|
|
else
|
|
tprintf (tty_dev, TRACE_CSRW, "Output data is %s\n",
|
|
fmt_char (inbound_value));
|
|
|
|
tty.io_data = LOWER_BYTE (inbound_value); /* set the data register from the lower byte */
|
|
break;
|
|
|
|
|
|
case ioPOPIO: /* Power-On Preset to I/O */
|
|
tty.flag_buffer = SET; /* set the flag buffer flip-flop */
|
|
break;
|
|
|
|
|
|
case ioCRS: /* Control Reset */
|
|
tty.control = CLEAR; /* clear the control flip-flop */
|
|
tty.flag_buffer = SET; /* and set the flag buffer flip-flop */
|
|
|
|
tty.mode = CN_INPUT; /* set the input mode and clear the print and punch modes */
|
|
|
|
tty.shift_in_data = MARK; /* indicate that the serial line is marking */
|
|
tty.cr_seen = FALSE; /* and that a CR character has not been seen */
|
|
break;
|
|
|
|
|
|
case ioCLC: /* Clear Control flip-flop */
|
|
tty.control = CLEAR; /* clear the control flip-flop */
|
|
break;
|
|
|
|
|
|
case ioSTC: /* Set Control flip-flop */
|
|
tty.control = SET; /* set the control flip-flop */
|
|
|
|
if ((tty.mode & CN_INPUT) == 0) { /* if the card is in output mode */
|
|
if (tty_dev.flags & DEV_REALTIME) /* then if the device is set for REALTIME mode */
|
|
print_unit.wait = TTY_REAL_TIME; /* then use the realistic timing delay */
|
|
else /* otherwise */
|
|
print_unit.wait = fast_data_time; /* use the optimized timing delay */
|
|
|
|
sim_activate (&print_unit, print_unit.wait); /* schedule the printer with selective punching */
|
|
|
|
tprintf (tty_dev, TRACE_SERV, "Unit delay %d service scheduled\n",
|
|
print_unit.wait);
|
|
}
|
|
break;
|
|
|
|
|
|
case ioSIR: /* Set Interrupt Request */
|
|
if (tty.control & tty.flag) /* if the control and flag flip-flops are set */
|
|
outbound.signals |= cnVALID; /* then deny PRL */
|
|
else /* otherwise */
|
|
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
|
|
|
|
if (tty.control & tty.flag & tty.flag_buffer) /* if the control, flag, and flag buffer flip-flops are set */
|
|
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
|
|
|
|
if (tty.flag == SET) /* if the flag flip-flop is set */
|
|
outbound.signals |= ioSRQ; /* then assert SRQ */
|
|
break;
|
|
|
|
|
|
case ioIAK: /* Interrupt Acknowledge */
|
|
tty.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
|
|
break;
|
|
|
|
|
|
case ioIEN: /* Interrupt Enable */
|
|
irq_enabled = TRUE; /* permit IRQ to be asserted */
|
|
break;
|
|
|
|
|
|
case ioPRH: /* Priority High */
|
|
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
|
|
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
|
|
|
|
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
|
|
outbound.signals |= ioPRL; /* then assert it unconditionally */
|
|
break;
|
|
|
|
|
|
case ioEDT: /* not used by this interface */
|
|
case ioPON: /* not used by this interface */
|
|
break;
|
|
}
|
|
|
|
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
|
|
} /* and continue until all signals are processed */
|
|
|
|
return outbound; /* return the outbound signals and value */
|
|
}
|
|
|
|
|
|
|
|
/* TTY local SCP support routines */
|
|
|
|
|
|
/* Set the keyboad input and print output filters.
|
|
|
|
This validation routine is called to configure the character input filter for
|
|
the keyboard unit and the output filter for the print unit. The "value"
|
|
parameter is one of the TT_MODE flags to indicate the type of filter desired.
|
|
"uptr" points at the unit being configured and is used to prohibit setting a
|
|
filter on the punch unit. The character and description pointers are not
|
|
used.
|
|
|
|
The routine processes commands of the form:
|
|
|
|
SET TTYn UC
|
|
SET TTYn 7B
|
|
SET TTYn 8B
|
|
SET TTYn 7P
|
|
|
|
...where "n" is 0 or 1 to set the filter on the keyboard or print units,
|
|
respectively. Mode 7P (7 bit with non-printing character suppression) isn't
|
|
valid for the keyboard and is changed to mode 7B (7 bit) if specified.
|
|
*/
|
|
|
|
static t_stat set_filter (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
|
|
{
|
|
if (uptr == &punch_unit) /* filters are not valid */
|
|
return SCPE_NOFNC; /* for the punch */
|
|
|
|
else {
|
|
if (uptr == &key_unit && value == TT_MODE_7P) /* non-printing character suppression */
|
|
value = TT_MODE_7B; /* isn't appropriate for the keyboard */
|
|
|
|
uptr->flags = uptr->flags & ~TT_MODE | value; /* set the filter to the requested mode */
|
|
return SCPE_OK; /* and return success */
|
|
}
|
|
}
|
|
|
|
|
|
/* Set the automatic line feed mode.
|
|
|
|
This validation routine is called when configuring the automatic line feed
|
|
mode. Some HP software systems expect the console terminal to transmit line
|
|
feed characters automatically following each carriage return. As an aid to
|
|
avoid typing LF characters after pressing ENTER, the SET TTY AUTOLF command
|
|
may be specified for the keyboard unit. This simulates pressing the AUTO LF
|
|
latching key on an HP 264x terminal. Specifying the SET TTY NOAUTOLF command
|
|
reverts to normal keyboard operation.
|
|
*/
|
|
|
|
static t_stat set_auto (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
|
|
{
|
|
if (uptr == &key_unit) /* if this is the keyboard unit */
|
|
return SCPE_OK; /* then allow the setting */
|
|
else /* otherwise auto LF mode is not valid */
|
|
return SCPE_NOFNC; /* for other units */
|
|
}
|
|
|
|
|
|
/* Set the timing mode.
|
|
|
|
This validation routine is called to set the printer and punch timing modes
|
|
to realistic or optimized timing. On entry, the "value" parameter is
|
|
DEV_REALTIME if real time mode is being set and zero if optimized ("fast")
|
|
timing mode is being set. The unit, character, and descriptor pointers are
|
|
not used.
|
|
*/
|
|
|
|
static t_stat set_mode (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
|
|
{
|
|
if (value == DEV_REALTIME) /* if realistic timing mode is selected */
|
|
tty_dev.flags |= DEV_REALTIME; /* then set the real-time flag */
|
|
else /* otherwise optimized timing mode is selected */
|
|
tty_dev.flags &= ~DEV_REALTIME; /* so clear the real-time flag */
|
|
|
|
return SCPE_OK; /* mode changes always succeed */
|
|
}
|
|
|
|
|
|
/* Enable or disable the TTY.
|
|
|
|
This validation routine is entered with "value" set to 1 for an ENABLE and 0
|
|
for a DISABLE. The unit, character, and descriptor pointers are not used.
|
|
|
|
If the TTY is already enabled or disabled, respectively, the routine returns
|
|
with no further action. Otherwise, if "value" is 1, the device is enabled by
|
|
clearing the DEV_DIS flag. If "value" is 0, a check is made to see if the
|
|
punch unit is attached to an output file. If it is, the disable request is
|
|
rejected; the unit must be detached first. Otherwise, the device is disabled
|
|
by setting the DEV_DIS flag.
|
|
|
|
In either case, the device is reset, which will restart or cancel the keyboad
|
|
poll, as appropriate.
|
|
*/
|
|
|
|
static t_stat set_endis (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
|
|
{
|
|
if (value) /* if this is an ENABLE request */
|
|
if (tty_dev.flags & DEV_DIS) /* then if the device is disabled */
|
|
tty_dev.flags &= ~DEV_DIS; /* then reenable it */
|
|
else /* otherwise the device is already enabled */
|
|
return SCPE_OK; /* so there's nothing to do */
|
|
|
|
else /* otherwise this is a DISABLE request */
|
|
if (tty_dev.flags & DEV_DIS) /* so if the device is already disabled */
|
|
return SCPE_OK; /* so there's nothing to do */
|
|
else if (punch_unit.flags & UNIT_ATT) /* otherwise if the punch is still attached */
|
|
return SCPE_ALATT; /* then it cannot be disabled */
|
|
else /* otherwise */
|
|
tty_dev.flags |= DEV_DIS; /* disable the device */
|
|
|
|
return tty_reset (&tty_dev); /* reset the TTY and restart or cancel polling */
|
|
}
|
|
|
|
|
|
/* Show the timing mode.
|
|
|
|
This display routine is called to show the current timing mode. The output
|
|
stream is passed in the "st" parameter, and the other parameters are ignored.
|
|
*/
|
|
|
|
static t_stat show_mode (FILE *st, UNIT *uptr, int32 value, CONST void *desc)
|
|
{
|
|
if (tty_dev.flags & DEV_REALTIME) /* if the current mode is real time */
|
|
fputs ("realistic timing", st); /* then report it */
|
|
else /* otherwise */
|
|
fputs ("fast timing", st); /* report that the optimized timing mode is active */
|
|
|
|
return SCPE_OK; /* the display routine always succeeds */
|
|
}
|
|
|
|
|
|
/* Reset the TTY.
|
|
|
|
This routine is called for a RESET, RESET TTY, RUN, or BOOT command. It is
|
|
the simulation equivalent of an initial power-on condition (corresponding to
|
|
PON, POPIO, and CRS signal assertion in the CPU) or a front-panel PRESET
|
|
button press (corresponding to POPIO and CRS assertion). SCP delivers a
|
|
power-on reset to all devices when the simulator is started.
|
|
|
|
If this is a power-on reset, the data buffer is cleared, the input shift
|
|
register is preset, and the default optimized output time is restored. If
|
|
the device is disabled, then the keyboard poll service is cancelled.
|
|
Otherwise, the poll is (re)started and coscheduled with the master keyboard
|
|
poll timer, and POPIO is asserted to the interface. In either case, any
|
|
print or punch operation in progress is cancelled.
|
|
*/
|
|
|
|
static t_stat tty_reset (DEVICE *dptr)
|
|
{
|
|
if (sim_switches & SWMASK ('P')) { /* if this is a power-on reset */
|
|
tty.io_data = NUL; /* then clear the data buffer */
|
|
tty.shift_in_data = MARK; /* and preset the input shift register */
|
|
|
|
fast_data_time = TTY_FAST_TIME; /* restore the initial fast data time */
|
|
}
|
|
|
|
if (tty_dev.flags & DEV_DIS) /* if the device is disabled */
|
|
sim_cancel (&key_unit); /* then cancel the keyboard poll */
|
|
|
|
else { /* otherwise */
|
|
key_unit.wait = hp_sync_poll (INITIAL); /* coschedule the poll with the poll timer */
|
|
sim_activate_abs (&key_unit, key_unit.wait); /* and begin keyboard polling */
|
|
|
|
io_assert (dptr, ioa_POPIO); /* PRESET the device */
|
|
}
|
|
|
|
sim_cancel (&print_unit); /* cancel any pending print or punch output */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
|
|
/* TTY local utility routines */
|
|
|
|
|
|
/* TTY input service routine.
|
|
|
|
The console input poll routine runs continuously while the device is enabled.
|
|
It is coscheduled with the general keyboard poll timer, which is a ten
|
|
millisecond calibrated timer that provides event timing for all of the
|
|
keyboard polling routines. Synchronizing the console poll with other
|
|
keyboard polls ensures maximum idle time.
|
|
|
|
Several HP operating systems require a CR and LF sequence for line
|
|
termination. This is awkward on a PC, as there is no LF key (CTRL+J is
|
|
needed instead). We provide an AUTOLF mode to add a LF automatically to each
|
|
CR input. When this mode is set, entering CR will set a flag, which will
|
|
cause a LF to be supplied automatically at the next input poll.
|
|
|
|
The 12531C teleprinter interface and the later 12880A CRT interface provide a
|
|
clever mechanism to detect a keypress during output. This is used by DOS and
|
|
RTE to allow the user to interrupt lengthy output operations to enter system
|
|
commands.
|
|
|
|
Referring to the 12531C schematic, the terminal input enters on pin X
|
|
("DATA FROM EIA COMPATIBLE DEVICE"). The signal passes through four
|
|
transistor inversions (Q8, Q1, Q2, and Q3) to appear on pin 12 of NAND gate
|
|
U104C. If the flag flip-flop is not set, the terminal input passes to the
|
|
(inverted) output of U104C and thence to the D input of the first of the
|
|
flip-flops forming the data register.
|
|
|
|
In the idle condition (no key pressed), the terminal input line is marking
|
|
(voltage negative), so in passing through a total of five inversions, a
|
|
logic one is presented at the serial input of the data register. During an
|
|
output operation, the register is parallel loaded and serially shifted,
|
|
sending the output data through the register to the device and -- this is
|
|
the crux -- filling the register with logic ones from U104C.
|
|
|
|
At the end of the output operation, the card flag is set, an interrupt
|
|
occurs, and the RTE driver is entered. The driver then does an LIA SC to
|
|
read the contents of the data register. If no key has been pressed during
|
|
the output operation, the register will read as all ones (octal 377). If,
|
|
however, any key was struck, at least one zero bit will be present. If the
|
|
register value doesn't equal 377, the driver sets the system "operator
|
|
attention" flag, which will cause DOS or RTE to output an asterisk prompt and
|
|
initiate a terminal read when the current output line is completed.
|
|
*/
|
|
|
|
static t_stat keyboard_service (UNIT *uptr)
|
|
{
|
|
t_stat status;
|
|
char input;
|
|
|
|
tprintf (tty_dev, TRACE_PSERV, "Poll delay %d service entered\n",
|
|
uptr->wait);
|
|
|
|
uptr->wait = hp_sync_poll (SERVICE); /* coschedule with the poll timer */
|
|
sim_activate (uptr, uptr->wait); /* and continue the poll */
|
|
|
|
if (tty.cr_seen && key_unit.flags & UNIT_AUTOLF) { /* if a CR was seen and auto-linefeed mode is active */
|
|
input = LF; /* then simulate the input of a LF */
|
|
|
|
tprintf (tty_dev, TRACE_XFER, "Character LF generated internally\n");
|
|
}
|
|
|
|
else { /* otherwise */
|
|
status = sim_poll_kbd (); /* poll the simulation console keyboard for input */
|
|
|
|
if (status < SCPE_KFLAG) /* if a character was not present or an error occurred */
|
|
return status; /* then return the poll status */
|
|
|
|
if (status & SCPE_BREAK) { /* if a break was detected */
|
|
input = NUL; /* then the character reads as NUL */
|
|
|
|
tprintf (tty_dev, TRACE_XFER, "Break detected\n");
|
|
}
|
|
|
|
else { /* otherwise */
|
|
input = (char) sim_tt_inpcvt ((int32) status, /* convert the character using the input mode */
|
|
TT_GET_MODE (uptr->flags));
|
|
|
|
tprintf (tty_dev, TRACE_XFER, "Character %s entered at keyboard\n",
|
|
fmt_char ((uint32) input));
|
|
}
|
|
}
|
|
|
|
tty.cr_seen = (input == CR); /* set the CR seen flag if a CR was entered */
|
|
|
|
if (tty.mode & CN_INPUT) { /* if the card is set for input */
|
|
tty.io_data = input; /* then store the character in the data register */
|
|
tty.shift_in_data = MARK; /* and indicate that the serial line is marking */
|
|
|
|
uptr->pos = uptr->pos + 1; /* count the character */
|
|
|
|
tty.flag_buffer = SET; /* set the flag buffer */
|
|
io_assert (&tty_dev, ioa_ENF); /* and the flag */
|
|
|
|
if (tty.mode & (CN_PRINT | CN_PUNCH)) /* if the printer or punch is enabled */
|
|
status = output ((int32) input); /* then scho the received character */
|
|
else /* otherwise */
|
|
status = SCPE_OK; /* silently indicate success */
|
|
}
|
|
|
|
else { /* otherwise the card is set for output */
|
|
tty.shift_in_data = input; /* and the received character will be shifted in */
|
|
status = SCPE_OK;
|
|
}
|
|
|
|
return status; /* return the status of the operation */
|
|
}
|
|
|
|
|
|
/* TTY output service routine.
|
|
|
|
The output service routine is scheduled when the interface receives an STC
|
|
signal. On entry, the data output register contains the character to output.
|
|
A prior control word will have enabled either the printer, the punch, both,
|
|
or neither. This selective output is handled by the "output" routine.
|
|
|
|
As noted in the comments above, if no key is pressed while output is in
|
|
progress, the input shift register will contain all ones, which is the
|
|
marking condition of the serial input line. However, if a key is pressed,
|
|
the register will contain something other than all ones; the exact value is
|
|
indeterminate, as it depends on the timing between the keypress and the print
|
|
operation.
|
|
*/
|
|
|
|
static t_stat print_punch_service (UNIT *uptr)
|
|
{
|
|
t_stat status;
|
|
|
|
tprintf (tty_dev, TRACE_SERV, "Printer and punch service entered\n");
|
|
|
|
status = output ((int32) tty.io_data); /* output the character if enabled */
|
|
|
|
if (status == SCPE_OK) { /* if the output succeeded */
|
|
tty.io_data = tty.shift_in_data; /* then shift the input line data into the buffer */
|
|
tty.shift_in_data = MARK; /* and indicate that the serial line is marking */
|
|
|
|
tty.flag_buffer = SET; /* set the flag buffer */
|
|
io_assert (&tty_dev, ioa_ENF); /* and the flag */
|
|
|
|
return SCPE_OK; /* return success */
|
|
}
|
|
|
|
else { /* otherwise an error occurred */
|
|
sim_activate (uptr, uptr->wait); /* so schedule a retry */
|
|
|
|
if (status == SCPE_STALL) /* if an output stall occurred */
|
|
return SCPE_OK; /* then ignore it */
|
|
else /* otherwise */
|
|
return status; /* return the operation status */
|
|
}
|
|
}
|
|
|
|
|
|
/* TTY print/punch output routine.
|
|
|
|
This routine outputs the supplied character to the print and/or punch unit as
|
|
directed by a prior control word sent to the interface. For output, the
|
|
control word may set the print flip-flop, the punch flip-flop, or both
|
|
flip-flops. These flip-flops generate the PRINT COMMAND and PUNCH COMMAND
|
|
output signals, respectively. Setting either one enables data transmission.
|
|
|
|
Only the HP 2754A (ASR35) teleprinter responds to the PRINT and PUNCH COMMAND
|
|
signals. All other terminals ignore these signals and respond only to the
|
|
serial data out signal (the paper tape punches on the 2749A and 2752A
|
|
teleprinters must be enabled manually at the console and operate concurrently
|
|
with the printers).
|
|
|
|
This routine simulates a 2754A when the punch unit (TTY unit 2) is attached
|
|
and a generic terminal when the unit is detached. With the punch unit
|
|
attached, the punch flip-flop must be set to punch, and the print flip-flop
|
|
must be set to print. These flip-flops, and therefore their respective
|
|
operations, are independent. When the punch unit is detached, printing will
|
|
occur if either the print or punch flip-flop is set. If neither flip-flop is
|
|
set, no output occurs. Therefore, the logic is:
|
|
|
|
if punch-flip-flop and punch-attached
|
|
then punch character
|
|
|
|
if print-flip-flop or punch-flip-flop and not punch-attached
|
|
then print character
|
|
|
|
Certain HP programs, e.g., HP 2000F BASIC FOR DOS-M/DOS III, depend on the
|
|
generic (2752A et. al.) behavior. The DOS and RTE teleprinter drivers
|
|
support text and binary output modes. Text mode sets the print flip-flop,
|
|
and binary mode sets the punch flip-flop. These programs use binary mode to
|
|
write single characters to the teleprinter and expect that they will be
|
|
printed. The simulator follows this behavior.
|
|
*/
|
|
|
|
static t_stat output (int32 character)
|
|
{
|
|
int32 print_char;
|
|
t_stat status = SCPE_OK;
|
|
|
|
if (tty.mode & CN_PUNCH /* if punching is enabled */
|
|
&& punch_unit.flags & UNIT_ATT) /* and the punch is attached */
|
|
if (fputc ((int) character, punch_unit.fileref) == EOF) { /* then write the byte; if the write fails */
|
|
cprintf ("%s simulator teleprinter punch I/O error: %s\n", /* then report the error to the console */
|
|
sim_name, strerror (errno));
|
|
|
|
clearerr (punch_unit.fileref); /* clear the error */
|
|
status = SCPE_IOERR; /* and stop the simulator */
|
|
}
|
|
|
|
else { /* otherwise the output succeeded */
|
|
punch_unit.pos = ftell (punch_unit.fileref); /* so update the file position */
|
|
|
|
tprintf (tty_dev, TRACE_XFER, "Data %03o character %s sent to punch\n",
|
|
character, fmt_char ((uint32) character));
|
|
}
|
|
|
|
if (tty.mode & CN_PRINT /* if printing is enabled */
|
|
|| tty.mode & CN_PUNCH /* or punching is enabled */
|
|
&& (punch_unit.flags & UNIT_ATT) == 0) { /* and the punch is not attached */
|
|
print_char = sim_tt_outcvt (character, /* then convert the character using the output mode */
|
|
TT_GET_MODE (print_unit.flags));
|
|
|
|
if (print_char >= 0) { /* if the character is valid */
|
|
status = sim_putchar_s (print_char); /* then output it to the simulation console */
|
|
|
|
if (status == SCPE_OK) { /* if the output succeeded */
|
|
print_unit.pos = print_unit.pos + 1; /* then update the file position */
|
|
|
|
tprintf (tty_dev, TRACE_XFER, "Character %s sent to printer\n",
|
|
fmt_char ((uint32) print_char));
|
|
}
|
|
}
|
|
|
|
else /* otherwise the character was filtered out */
|
|
tprintf (tty_dev, TRACE_XFER, "Character %s discarded by output filter\n",
|
|
fmt_char ((uint32) character));
|
|
}
|
|
|
|
return status; /* return the status of the operation */
|
|
}
|