/* 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 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 */ }