book.mcsa.compin 10/18/84 1051.8rew 08/05/83 1319.5 5202 .ifi init_plm "AN85-01" .srv Charsw -1 .ifi toc_on .ifi fig_on .ifi tp.mcsa.compin .ifi pf.mcsa.compin .ifi s1.mcs.compin .ifi s2.mcs.compin .ifi s3.mcs.compin .ifi s4.mcs.compin .ifi s5.mcs.compin .ifi s6.mcs.compin .ifi s7.mcs.compin .ifi s8.mcs.compin .ifi s9.mcs.compin .ifi s10.mcsa.compin .ifi s11.mcsa.compin .ifi s12.mcsa.compin .ifi s13.mcsa.compin .ifi s14.mcsa.compin .ifi s15.mcsa.compin .ifi s16.mcs.compin .ifi s17.mcsa.compin .ifi appa.mcs.compin .ifi appb.mcsa.compin .ifi appc.mcs.compin .ifi appd.mcs.compin .ifi appe.mcs.compin .brp .ifi book.mcsa.toc.compin  s1.mcs.compin 10/18/84 1051.8rew 12/09/81 1148.8 127629 .ifi init_plm "AN85-01" .srv section 1 .pdl 68 .ifi l0h "Overview of Ring 0 Multics Communication System" .ifi l1h "Introduction" The Multics Communication System is responsible for the transmission of characters between the Multics virtual memory and various user equipment (particularly terminals) connected by means of telecommunications channels to the Front-End Network Processor (FNP). The software constituting Multics Communication System resides partly in ring zero of the central system (CS) and partly in the FNP itself. Communication between the two computers (the CS and the FNP) takes place over the Direct Interface Adapter (DIA), a peripheral device of the FNP. A Multics CS may be connected to up to four FNPs simultaneously. The FNP for which Multics Communication System was originally designed was called the DATANET 355, which is why the strings "355" and "dn355" appear in the names of many of the programs and data bases described in this document. .ifi hit "K|responsibilities~Multics Communication System~character transmission" .ifi hit "K|responsibilities~central system" .ifi hit "K|central system (CS)~responsibilities" .spb 2 This manual is intended to provide a general understanding of the workings of Multics Communication System. For complete details of implementation, the reader should examine the source code itself. .spb 2 Section 1 through Section 8 of this manual describe the portion of Multics Communication System that is resident in the CS; Sections 9 through 17 describe the portion resident in the FNP. The appendices contain details of implementation such as command descriptions, specific codes used in various contexts, etc. .ifi l1h "Responsibilities of the CS" The primary responsibilities of the CS with respect to communications are in the following areas: .spb .inl 10 .unl 5 1. The association of communications channels (e.g., the data path to a user's terminal) with Multics processes; .ifi hit "K|communications channels" .ifi hit "K|Multics processes" .spb .unl 5 2. dispatching input from the various communications channels to the appropriate processes; .spb .unl 5 3. converting output supplied by Multics processes to a form suitable for sending to its destination (e.g., terminals); .spb .unl 5 4. management of the ring 0 terminal I/O buffer space (tty_buf); .ifi hit "K|terminal I/O~buffer space" .spb .unl 5 5. multiplexing and demultiplexing the subchannels of a concentrator channel; .ifi hit "K|concentrator~channel" .spb .unl 5 6. management of the FNP, i.e., loading it at system initialization time, recovering when it crashes, etc. .inl 0 .ifi l1h "Structure of the CS Portion of Multics Communication System" .ifi hit "K|FNP~interface modules" .ifi hit "K|module~user interface" .ifi hit "K|multiplexing" .ifi hit "K|interface modules~FNP" .ifi hit "K|interface modules~user" The major components of the CS portion of Multics Communication System are the FNP interface modules, three user interface modules (tty_write, tty_read, and tty_index), and interfaces between levels of multiplexing. Other components include utility routines for the management of locks and buffer space; initialization routines; and special subroutines used by the user interface modules. Brief descriptions of the major components appear below; all the components are discussed in more detail in Sections 4-8. The multiplexing mechanism is described in Section 3. .ifi l2h "FNP Interface Modules" .ifi hit "K|dn355" The standard FNP interface module is dn355; it is the only program in the system that communicates directly with the FNP (except for the routines that load and dump the FNP). If a root-level multiplexer (see Section 3) other than a DATANET 6600 or DATANET 6670 running Multics Communication System is used, a site-supplied module interfacing to this multiplexer would replace dn355. Except as otherwise noted, the discussions in this manual assume that the standard module is being used. .spb 2 .ifi hit "K|fnp_multiplexer" The dn355 module is invoked in either of two ways: it is called from tty_write and tty_index through the FNP multiplexer module, fnp_multiplexer, to send output data and control information, respectively, to the FNP; and it processes interrupts from the FNP when the latter sends input data or control information. Control information is passed between the FNP and the CS by means of a wired segment named dn355_mailbox; user data is read into and written from a wired segment named tty_buf. These two segments are described in more detail in Section 2. .ifi hit "K|module~FNP multiplexer" .ifi hit "K|module~FNP interface" .ifi hit "K|module~user interface" .ifi hit "S|user interface modules~see module" .ifi hit "S|FNP~interface modules~see module" .spb 2 In general, all I/O over the DIA is initiated by the FNP; the only kind of I/O operation initiated by dn355 is an interrupt that instructs the FNP software to read a submailbox of dn355_mailbox. The details of this mechanism are spelled out in Sections 4 and 13. .ifi l2h "User Interface Modules" .ifi hit "K|module~user interface" .ifi hit "K|module~tty_write" .ifi hit "K|module~tty_read" .ifi hit "K|module~tty_index" The three user interface modules, tty_write, tty_read, and tty_index, are called from the user ring through entries in the gate hcs_. User processes usually do this through the I/O switch mechanism, i.e., by calling entries in iox_ which call entries in the user-ring terminal I/O module, tty_. The answering service, which requires more direct control over the channel, calls the hcs_ entries directly. See also the U__s_e_r R__i_n_g I__n_p_u_t/_O__u_t_p_u_t PLM, Order No. AN57. .spb 2 The uses of these three modules are explained briefly below; they are described in more detail in Section 5. .ifi l3exact "tty_write MODULE" .ifi l3toc "tty_write Module" .ifi hit "K|tty_write" The tty_write module prepares user-supplied output for transmission to the FNP, places the output in tty_buf, and calls channel_manager$write to initiate the transmission. If there is insufficient space in tty_buf to hold all of the supplied output at once, tty_write only processes part of it; the caller generally goes blocked in this case, and receives a wakeup when Multics Communication System is ready to handle the rest of the output. .ifi l3exact "tty_read MODULE" .ifi l3toc "tty_read Module" .ifi hit "K|tty_read" .ifi hit "K|wakeup" A user process that is ready to handle terminal input calls one of several entries in tty_read, which copies whatever input is available from the specified channel into a buffer supplied by the caller. If there is no available input from the channel, the caller normally goes blocked; a wakeup is sent when input arrives from the FNP. .ifi l3exact "tty_index MODULE" .ifi l3toc "tty_index Module" .ifi hit "K|tty_index" The tty_index module contains a variety of entries concerned with control of a channel or the data bases associated with it. The tty_index, tty_attach, tty_event, tty_detach, and tty_new_proc entries deal with the associations between communications channels and processes; the tty_order and tty_abort entries are used to send control information about the channel to the FNP and to modify the treatment by Multics Communication System of input and output data. .ifi l2h "Multiplexing Interfaces" .ifi hit "K|channel_manager" .ifi hit "K|priv_channel_manager" .ifi hit "K|module~call-switching" .ifi hit "K|multiplexing interfaces" Two call-switching modules, channel_manager and priv_channel_manager, are used to pass calls and interrupts from one level of multiplexing to the next. They are described in more detail in Section 3. .spb 2 The standard FNP multiplexer module, fnp_multiplexer, is called by channel_manager to send output and control information to an FNP. Its task is to format an FNP mailbox and pass it on to dn355. .ifi hit "K|fnp_multiplexer" .ifi hit "K|module~multiplexer" .ifi hit "S|multiplexer~module~see module" .spb 2 The interrupt handler for nonmultiplexed channels is tty_interrupt. This is the module that sends wakeups to user processes when input arrives or output finishes. .ifi l1h "Some Standard Sequences" .ifi l2h "Channel Initialization" .ifi hit "K|answering service" .ifi hit "K|channel definition table (CDT)" .ifi hit "K|dialups" .ifi hit "K|initialization~channel" .ifi hit "K|initialization~answering service" .ifi hit "K|channel definition table (CDT)" As each FNP or concentrator channel is initialized, the answering service takes control of each subchannel of the FNP or concentrator as defined in the channel definition table (CDT) by calling tty_attach for each one. After a channel is attached, the answering service issues a "listen" request by calling tty_order; tty_order forwards the request to fnp_multiplexer, which encodes it into a mailbox to send to the FNP. Once the FNP has received a "listen" request for the channel, it is prepared to accept dialups from the channel. .ifi l2h "Dialup" When a connection is established between the FNP and a communications channel, the FNP sends a mailbox to the CS with the operation code "accept new terminal" (see Appendix A for a description of mailbox operation codes). .spb 2 The dn355 module sends a mailbox back to the FNP saying "terminal accepted," and forwards the interrupt to tty_interrupt, which wakes up the answering service to inform it of the dialup. If a user then logs in from the newly-dialed-up terminal, a process is created, and the answering service "lends" the channel to this process by calling tty_new_proc, thus establishing the new process as the "user" of the channel. .ifi l2h "Output" .ifi hit "K|channel~output" When the user of the channel calls tty_write, his data (or as much of it as can be handled at once) is copied first into the internal buffers of tty_write (see Section 5); tty_write then performs the necessary conversions and translations, copies the data into tty_buf (either starting an output chain for the channel, or appending to an already existing one), and calls channel_manager$write. The call is eventually forwarded to dn355, which sends a mailbox to the FNP telling it that there is output for the channel, and also telling how much and where in tty_buf it is; the FNP then copies the output from tty_buf into FNP memory in preparation for transmitting it to the channel. Once the FNP has copied the output, dn355 frees the buffers composing the output chain in tty_buf. .ifi l2h "Input" .ifi hit "K|break character" When a "break" character (i.e., an end-of-message character, typically a newline) is input, the FNP sends a mailbox informing the CS that input of a certain length has been received over the channel. If there is room for it, dn355 replies with a mailbox instructing the FNP to copy the input into the circular buffer in the header of tty_buf (see Section 2); once the FNP I/O has completed, dn355 allocates an input chain in tty_buf, copies the data into this chain from the circular buffer and sends an "accept input" interrupt to tty_interrupt. If the process using the channel has unsuccessfully attempted to obtain input from the channel (as indicated by a flag in the wired terminal control block (WTCB)), a wakeup is sent to inform it that the input has arrived. .ifi hit "K|wakeup" .spb 2 When the user process calls tty_read (either because it received the wakeup, or simply because it was ready to receive input), the data is copied into the internal buffers of tty_read, translated and converted appropriately, and copied into the buffer supplied by the caller. Once it has processed the input, tty_read frees the input chain buffers in tty_buf. .brf .brn 10 .ifi l2h "Quit" .ifi hit "K|hndlquit mode" .ifi hit "K|quit condition" When a user presses the ATTENTION or INTERRUPT key at a terminal, a "line break" condition is generated, which is recognized by the FNP and reflected back to the CS. If the channel is in "hndlquit" mode (which it usually is), dn355 discards any pending output currently in tty_buf intended for the physical channel. The interrupt is then forwarded through channel_manager to tty_interrupt, which discards any pending input and output from or to the logical channel. If quits have been "enabled" through the use of the quit_enable control operation (as they almost always are), tty_interrupt calls pxss$ips_wakeup_int, which causes the "quit" condition to be signalled in the user process. .ifi l2h "Hangup" .ifi hit "K|hangup" A hangup (disconnection of the channel) can be initiated by the CS (as a result of a "hangup" control request sent through tty_order), in which case the FNP takes action to disconnect the channel; or the FNP may detect that the channel has disconnected, as a result of either deliberate action by the user of the terminal or failure of the communications equipment. In either case, once the FNP is satisfied that the connection has been broken, it sends a mailbox with the operation code "terminal disconnected." On receiving this mailbox, dn355 forwards the interrupt to tty_interrupt, which sends a wakeup to the "owning" process (generally the answering service) and frees any input and output chains associated with the channel. After receiving the hangup wakeup, the answering service usually issues another "listen" request so that the channel can be dialed up again. .ifi hit "K|crash interrupt" .ifi hit "K|wakeup" .spb 2 If the channel that hung up is a multiplexed channel, the interrupt handler for that channel sends a "crash" interrupt to tty_interrupt for each currently active subchannel. These interrupts are handled exactly like hangup interrupts, except that tty_interrupt does not wake up the owning process, since the subchannels cannot be listened to again until the multiplexer is reconnected. The interrupt handler for the major channel does wake up the answering service, which then takes appropriate action to reinitialize the major channel and its subchannels, as described in Sections 3 and 7. .fin .inl 0 .brp  s2.mcs.compin 10/18/84 1051.8rew 12/09/81 1148.9 172215 .ifi init_plm "AN85-01" .srv section 2 .pdl 68 .ifi l0h "Hardcore Data Bases" .ifi l1h "Data Bases Used for Communication with FNP" Two wired segments are used by the Multics hardcore for communication with, and storing information about, the various configured FNPs. These segments are dn355_data, which describes the FNP configuration and current status of each FNP, and dn355_mailbox, which is used for direct communication with the FNP as described in Section 4. .ifi hit "K|dn355_data" .ifi hit "K|dn355_mailbox" .ifi l1exact "dn355_data" .ifi l1toc "dn355_data" The format of dn355_data is described in the include file dn355_data.incl.pl1. The first part of the segment contains general configuration information; the remainder consists of one block of FNP-specific information for each configured FNP. This block (called fnp_info) is the multiplexer database for the FNP; a pointer to the corresponding block is kept in each FNP's LCT entry (see the discussion of the LCT later in this section) and passed to the various entries in fnp_multiplexer. Included in the fnp_info block is a pointer to the FNP's mailbox area and a pointer to an array of physical channel blocks (PCBs) containing information relevant to the individual subchannels of the FNP. .ifi hit "K|fnp_info" .ifi hit "K|multiplexer~database" .ifi l1exact "dn355_mailbox" .ifi l1toc "dn355_mailbox" This segment contains a mailbox area for each configured FNP. 300(8) words are reserved for each mailbox area. .ifi hit "K|mailbox~area" .spb 2 Each mailbox area consists of an 8-word header followed by twelve submailboxes. The submailboxes are used for individual communications between the FNP and the CS. The first eight submailboxes are eight words each, and are used for transactions originating in the CS. The remaining four submailboxes are 28 words each, and are used for transactions originating in the FNP. See Section 4 for further information on the use of submailboxes. .ifi hit "K|submailbox" .spb 2 The formats of the mailbox header and the submailboxes are described in the include file dn355_mailbox.incl.pl1. The mailbox header includes the following items: the peripheral control word (PCW) used to send interrupts to the FNP over the DIA; an array of flags indicating which CS-controlled submailboxes are currently in use; and a "terminate interrupt multiplex word" (timw) used by the FNP to indicate that it has processed a particular submailbox. Each bit that is on in the timw corresponds to a submailbox used by the FNP since the last time dn355 examined it. The mailbox header also contains two words of "crash data" that are filled in by the fault handler of the FNP when the FNP crashes, giving the type of fault that caused the crash, the FNP instruction counter at the time of the fault, and, if applicable, encoded information enabling dn355 to find an appropriate error message in dn355_messages (see Section 8). .ifi hit "K|peripheral control word (PCW)" .ifi hit "K|terminate interrupt multiplex word (timw)" .spb 2 Each submailbox contains an I/O command and an operation code (opcode). The I/O command indicates which of four classes of operations the submailbox specifies; the opcode indicates the specific operation. The four possible I/O command values are: .ifi hit "K|I/O command" .ifi hit "K|operation code (opcode)" .spb .inl 9 .unl 4 1 = read control data (rcd): .ifi hit "K|read control data (RCD)" .brf control information being passed from the FNP to the CS. .spb .unl 4 2 = read text (rtx): .ifi hit "K|read text (RTX)" .brf terminal input being passed from the FNP to the CS. .spb .unl 4 3 = write control data (wcd): .ifi hit "K|write control data (WCD)" .ifi hit "S|WCD~see write control data" .brf control information being passed from the CS to the FNP. .spb .unl 4 4 = write text (wtx): .ifi hit "K|write text (WTX)" .brf terminal output being passed from the CS to the FNP. .spb .inl 0 The opcodes are summarized in Appendix A. Opcodes and I/O commands are defined in mailbox_ops.incl.pl1. .spb 2 A submailbox may also contain additional information describing the operation in detail. The meaning of this additional information varies according to the opcode. Each submailbox includes in its low-order 18 bits a checksum which is the sum of all the 9-bit bytes in the remainder of the submailbox. .ifi hit "K|checksum" .ifi l1exact "tty_buf" .ifi l1toc "tty_buf" .ifi hit "K|tty_buf" .ifi hit "K|configuration~deck" The wired segment tty_buf is used by all portions of the hardcore communications system. It contains information of general interest to Multics Communication System, the control blocks associated with each communications channel, and the pool of free space in which input and output buffers are allocated. Its size can be set by means of a PARM TTYB card in the configuration deck, e.g.: .spb 2 PARM TTYB 8192. .spb 2 The default size is 5120 words (5K). .spb 2 The layout of tty_buf is as follows: the first part is a header, containing information about the current state of the communications system and a variety of metering information; then comes a circular buffer into which terminal input is written by the FNP; following this is the logical channel table (LCT), allocated during initialization. The remainder of the segment is free space, in which are allocated data buffers, various control blocks and delay queues. .ifi hit "K|circular buffer" .ifi hit "K|logical channel table (LCT)" .ifi l2exact "tty_buf Header" .ifi l2toc "tty_buf Header" .ifi hit "K|tty_buf~header" .ifi hit "K|tty_meters command" .ifi hit "K|free~space" .ifi hit "K|tty_space_man" The format of the tty_buf header is described in the include file tty_buf.incl.pl1. It contains control words giving the origin and length of the free space pool, the size of the circular buffer, and the address of the LCT. It also is used to store a variety of metering information relating to Multics Communication System, which is copied out as needed by the tty_meters command. The first word of the tty_buf header is a lock used by tty_space_man to ensure that no two processors attempt to update the free space pool at the same time. .ifi l2h "Circular Buffer" .ifi hit "K|circular queue" .ifi hit "K|configuration~deck" The circular buffer (also called the circular queue) is a buffer in which long input messages are stored directly by the FNP. (Short messages are sent in submailboxes; see Section 4 for more information.) Its size may be set by a PARM TTYQ card in the configuration deck, e.g.: .spf .inl 10 PARM TTYQ 1024. .spf .inl 0 The default size is 256 words. The circular buffer cannot be smaller than 256 words. .ifi l2h "Logical Channel Table (LCT)" .ifi hit "K|LCT entries (LCTEs)" .ifi hit "K|channel definition table (CDT)" The logical channel table (LCT) consists of a header and an array of LCT entries (LCTEs). There is an LCTE for every channel (at each level of multiplexing) defined in the CDT. The array is allocated in tty_buf at system initialization time; spare entries may be allocated in case the number of configured channels is to be increased while the system is running. The number of such spare entries is determined by the spare_channel_count keyword in the CMF; the default is 10. The format of the LCT is described in the include file lct.incl.pl1. .spb 2 The LCT header contains the size of the array of LCTEs, a pointer to the logical channel name table (LCNT), which is discussed later in this section, and a global lock for the delay queues (see the discussion of locking and queuing in Section 6). .ifi hit "K|logical channel name table (LCNT)" .spb 2 The position of a channel's LCTE in the LCTE array is that channel's device index (devx), which is the number used throughout ring zero to identify the channel. The contents of an LCTE include: the "multiplexer type" of the channel (e.g., FNP, "tty" or nonmultiplexed channel, etc.); a pointer to the database associated with the channel; the devx of the channel's parent multiplexer or "major channel"; the subchannel of the major channel that this channel represents; the devx of the physical channel (i.e., subchannel of an FNP) of which this channel is a descendant (which is the same as the channel's own devx if the LCTE is that of a physical FNP channel); a lock used to ensure that the LCTE is never modified by more than one process at a time; and various flags. An entry-in-use flag is used to indicate whether the LCTE is currently valid; this flag is turned on when the channel is initialized, and turned off when it is terminated. (See the discussions of multiplexing in Section 3 and initialization in Section 7 for more details.) .ifi hit "K|device index (devx)" .ifi hit "K|multiplexer~type" .ifi hit "K|parent multiplexer" .ifi hit "K|subchannel" .ifi hit "K|major channel" .ifi hit "K|physical channel" .ifi hit "K|multiplexer~subchannel" .ifi hit "K|subchannel~multiplexer" .ifi hit "K|module~multiplexer" .ifi hit "S|LCNT~see logical channel name table" .spb 2 The information in the LCTE is used by channel_manager (described in Section 3) to determine what multiplexer module to forward calls to, and to enable it to identify the multiplexer channel and subchannel to the called entry. .ifi hit "K|multiplexer~module" .ifi l2h "Dynamic Portion of tty_buf" The remainder of tty_buf is allocatable space, which may be used for any of the following purposes: .spf .bba .inl 10 .unl 5 1o free block .brf .unl 5 1o data buffer (input or output) .brf .unl 5 1o per-channel control block .brf .unl 5 1o output pseudo-DCW list .brf .unl 5 1o delay queue entry .inl 0 .bea .ifi l3h "Free Block" .ifi hit "K|free~block" .ifi hit "K|tty_space_man" A free block is a contiguous block of any even number of words that is available for allocation by tty_space_man (see the discussion of space management in Section 6). It has a header that contains its size in words and the offset in tty_buf of the next free block. Free blocks are chained together in order of increasing address; a word in the tty_buf header contains the offset of the first free block in the chain. Adjacent free blocks are combined into larger blocks as they become free. .ifi l3h "Data Buffer" .ifi hit "K|data buffer" .ifi hit "K|input chain" .ifi hit "K|output chain" .ifi hit "K|size code" .ifi hit "K|tally" A data buffer contains either input characters that have come from a channel or output characters that are to be sent to a channel. The input or output for a particular channel are organized into chains of such buffers (called input chains and output chains). Each buffer in such a chain is a block whose size is a multiple of 16 words, up to a limit of 128 words. The first word of a buffer is a control word containing the offset of the next buffer in the chain, a size code indicating the size of the buffer, the tally of valid characters actually present in the buffer, and some flags. The remainder of the buffer contains data charaters. The size code has a value between 0 and 7 inclusive, and is one less than the size of the buffer in multiples of 16 words; in other words, a code of 0 indicates a size of 16 words, a code of 1 indicates a size of 32 words, and a code of 7 indicates a size of 128 words. The format of a data buffer is described in the include file tty_buffer_block.incl.pl1. .ifi l3h "Per-Channel Control Blocks" .ifi hit "K|control blocks~per-channel" Per-channel control blocks are allocated as needed in tty_buf when each channel is initialized. The format of such a control block depends on the type of the channel. Two standard formats are described here: the wired terminal control block and the physical channel block. .ifi l4h "Wired Terminal Control Block (WTCB)" .ifi hit "K|wired terminal control block (WTCB)" .ifi hit "K|nonmultiplexed channel" .ifi hit "S|WTCB~see wired terminal control block" The wired terminal control block (WTCB) is the primary database for a nonmultiplexed channel; the database pointer in the LCTE of such a channel points to its WTCB. The WTCB is used by tty_read, tty_write, tty_index, and tty_interrupt. Its format is described in the include file wtcb.incl.pl1. .spb 2 .ifi hit "K|owning process" .ifi hit "K|user process" .ifi hit "K|tty_interrupt" The WTCB contains all information about the current state of the channel that is ever needed or modified by tty_interrupt, which cannot reference unwired data. This information includes the identification of the "owning" and "user" processes for the channel, and the event channels used for waking up these processes; the baud rate and line type of the channel, which are set at dialup time; the offsets of any currently-active input and output chains; and various flags. The WTCB also contains a pointer to the unwired terminal control block (TCB), described later in this section. .ifi l4h "Physical Channel Block (PCB)" .ifi hit "K|physical channel block (PCB)" The physical channel block (PCB) contains information specific to a physical subchannel of an FNP. It is used by fnp_multiplexer and dn355. The format of the PCB is described in the include file pcb.incl.pl1. .spb 2 The PCBs for the subchannels of an FNP are allocated in a contiguous array when the FNP is initialized; the fnp_info block for each FNP contains a pointer to the beginning of the PCB array for that FNP. The subchannel number of each physical channel is the index into the PCB array of that channel's PCB. .ifi hit "K|fnp_info" .ifi hit "K|subchannel number" .spb 2 The PCB contains flags describing the current state of the physical channel, and pointers to any output chain that has been passed to fnp_multiplexer but not yet sent over the DIA. .ifi l3h "Pseudo-DCWs" .ifi hit "K|pseudo-DCW list" .ifi hit "K|output chain" An output "pseudo-DCW" list is used to send an FNP the addresses and tallies of the individual buffers in an output chain. An output chain is described by an array of up to 16 pseudo-DCWs in a single allocated block. Each pseudo-DCW occupies one word, containing the absolute address of an output buffer and the number of characters in the buffer. .ifi l3h "Delay Queue Entries" .ifi hit "K|delay~queue" .ifi hit "K|LCTE" The delay queue for a channel contains entries describing interrupt events for that channel that could not be processed because the channel's LCTE was locked at the time of the interrupt. These queue entries are processed when it is time to unlock the LCTE (see Section 6 for details). A delay queue entry contains the offset in tty_buf of the next entry in the channel's queue (if any) and the interrupt type and data that were passed to channel_manager$interrupt (see Section 3). If a delay queue exists for a channel, the offsets of its first and last entries appear in the channel's LCTE. .ifi hit "S|PCB~see physical channel block" .ifi hit "S|TCB~see terminal control block" .ifi l1exact "tty_area" .ifi l1toc "tty_area" .ifi hit "K|tty_area" The unwired ring-zero segment tty_area is used for control blocks that are not needed by any wired programs. It is managed by means of the PL/I area mechanism. .spb 2 .ifi hit "K|logical channel name table (LCNT)" .ifi hit "K|terminal control block (TCB)" Two types of databases are allocated in tty_area: the logical channel name table (LCNT) and (unwired) terminal control blocks (TCBs). .ifi hit "K|terminal control block (TCB)" .ifi l2h "Logical Channel Name Table (LCNT)" The LCNT is an array of channel names. Each name in the LCNT occupies the same relative position as the named channel's LCTE in the LCT. Thus the LCNT can be used to derive a channel's name from its devx or vice versa. The format of channel names is described in M_P_M_ C__o_m_m_u_n_i_c_a_t_i_o_n_s I__n_p_u_t/_O__u_t_p_u_t, Order No. CC92. .ifi hit "K|channel~name" .ifi hit "K|device index (devx)" .ifi hit "S|devx~see device index" .ifi l2h "Terminal Control Block (TCB)" .ifi hit "K|terminal control block (TCB)" There is a TCB for each initialized nonmultiplexed channel. It contains information used at call time for the conversion and translation of input and output data as described in Section 5. The format of the TCB is described in the include file tcb.incl.pl1. The TCB for each channel is found by following a pointer in the channel's WTCB (see above). .ifi hit "K|terminal control block (TCB)" .spb 2 The TCB includes pointers to the various conversion and translation tables kept in tty_tables (discussed below). For each type of table, two relative pointers are kept in the TCB: a current table pointer and a default table pointer. The default table pointer is used to reset the current table to the default for the channel's terminal type. If the default table pointer is -1, the current table is the default table; otherwise, the current table has been set explicitly, and the default table is made the current table if an order is made to set the table to its default value. .ifi hit "K|conversion~and translation tables" .ifi l1exact "tty_tables" .ifi l1toc "tty_tables" .ifi hit "K|tty_tables" The unwired segment tty_tables contains all the tables to be used in converting terminal input and output between the form stored internally and the form in which it is received from or sent to the terminal. These tables are supplied by user-ring programs by means of the set_input_translation, set_output_translation, set_input_conversion, set_output_conversion, set_special, and set_delay orders. .spb 2 The tty_tables segment is managed by a utility program named tty_tables_mgr, which is called by tty_index. The segment consists of a header and an area in which the tables are allocated. Each table in the area is preceded by a descriptor that identifies the table type and links tables of that type in a list. Six table types are supported: .ifi hit "K|tty_tables_mgr" .ifi hit "K|table types" .ifi hit "K|table descriptor" .spb .inl 10 .unl 5 1. input translation .unl 5 .spb 2. output translation .unl 5 .spb 3. input conversion .unl 5 .spb 4. output conversion .unl 5 .spb 5. special .unl 5 .spb 6. delay .inl .spb The tty_tables header contains a relative pointer to the first table of each type. The format of the tty_tables header and the table descriptor are described in the include file tty_tables.incl.pl1; the formats of the tables themselves are described in the include file tty_convert.incl.pl1; the tables are discussed in more detail in the description of the tty_ I/O module in M_P_M_ C__o_m_m_u_n_i_c_a_t_i_o_n_s I__n_p_u_t/_O__u_t_p_u_t, Order No. CC92. .spb 2 The tables are shared as necessary, i.e., if two channels are using identical tables of any type, only one copy of the table is kept in tty_tables. Accordingly, a reference count is kept in the table descriptor to indicate how many TCBs contain pointers to the table; a table is not freed until the reference count goes to zero. .ifi hit "K|table descriptor" .fin .inl 0 .brp  s4.mcs.compin 10/18/84 1051.8rew 12/09/81 1148.9 173097 .ifi init_plm "AN85-01" .srv section 4 .ifi l0h "FNP Interface Module" .ifi hit "K|direct interface adapter (DIA)" .ifi hit "S|DIA~see direct interface adapter" .ifi hit "K|dn355" .ifi hit "K|mailbox~area" All communication with the FNP over the Direct Interface Adapter (DIA) is managed by the dn355 module. Control information is passed between the two computers by means of the mailbox segment, dn355_mailbox. A pointer to the mailbox area used for each FNP is set in dn355_data at system initialization time by fnp_init. The mailbox area is described in Section!2. .spb 2 When Multics Communication System was originally designed, one of the design goals was compatibility with another FNP software package called NPS. This goal was subsequently abandoned, but some of the compatibility features remain; this is the source of some peculiarities in the FNP/CS interface, such as the use of some apparently unnecessary mailbox operation codes and the presence of some unused fields in the submailbox. .ifi l1h "Calls and Interrupts" There are four entry points to dn355: dn355$send_wcd and dn355$send_wcd_global, which are called by fnp_multiplexer to send output or control information to the FNP; dn355$interrupt, which is invoked when an interrupt arrives from the FNP; and dn355$hangup_fnp_lines, which is called when an FNP is shut down. .spb 2 .ifi hit "K|mailbox~operation code" .ifi hit "K|command data" .ifi hit "K|submailbox" The normal "called" entry (dn355$send_wcd) is passed a mailbox operation code and optional command data, which is copied into the lowest-numbered available submailbox to be sent to the FNP; it then calls the internal procedure send_mbx to interrupt the FNP at the level corresponding to the submailbox. If the operation being performed is the transmission of output data, some more processing is required before .brn 4 sending the mailbox, as described later in this section. The send_wcd_global entry is used for control information that is not specific to a particular channel. .spb 2 .ifi hit "K|interrupt" An interrupt comes from the FNP under either of the following circumstances: .spb .inl 10 .unl 5 .bba 1o the FNP has filled in or completed processing of a submailbox, and wants the CS to look at that submailbox; .spb .unl 5 1o the FNP has crashed and sent an "emergency" interrupt. .bea .spb 2 .inl 0 .ifi hit "K|interrupt~level" .ifi hit "K|terminate interrupt multiplex word (timw)" .ifi hit "S|TIMW~see terminate interrupt multiplex word" If the FNP has crashed, the interrupt level (passed to dn355$interrupt in its fourth argument) is 7; in this case, words 6 and 7 of the mailbox header describe the cause of the crash. If the interrupt level is 3 (for a "normal" FNP interrupt), dn355 checks the "terminate interrupt multiplex word" (TIMW) in the mailbox header to see if the FNP has processed a submailbox, as explained in more detail later in this section. .spb 2 It should be noted that, each time dn355$interrupt runs, there may have been more than one interrupt from the FNP since the last time it ran; for instance, interrupts may have been masked when the FNP interrupt occurred (dn355$send_wcd usually runs with interrupts masked). Accordingly, dn355$interrupt takes care of all pending business (submailboxes processed by the FNP) every time it runs. .ifi l1h "Mailbox Transactions" .ifi l2h "Transactions Initiated by the FNP" .ifi l3h "FNP-Controlled Mailboxes" .ifi hit "K|FNP~initiated transactions" .ifi hit "K|read control data (RCD)" .ifi hit "K|terminate interrupt multiplex word (timw)" Mailboxes 8 through 11 are reserved for FNP-initiated transactions; it is the responsibility of the FNP to know which of these mailboxes are currently available. Whenever the FNP needs to initiate a transaction with the CS, it selects one of these mailboxes and fills it in with the FNP line number of the relevant channel, an RCD (read control data) I/O command, the operation code describing the particular transaction, and any associated command data. It then writes the submailbox into CS memory, turns on the bit in the TIMW in the mailbox header corresponding to the submailbox, and sends an interrupt to the CS. .ifi l3h "Processing the RCD" When dn355 runs as a result of the abovementioned interrupt it examines any submailboxes corresponding to 1-bits in the TIMW. In the case of the mailbox used in the preceding paragraphs, it sees the RCD I/O command and knows that it must process the operation code supplied by the FNP. For most operation codes, such as "accept new terminal", "disconnected line", or "break condition", this processing is simple and straightforward; it consists primarily of forwarding the interrupt to channel_manager$interrupt, and in some cases rewriting the mailbox with an appropriate acknowledging operation code. The interrupt is eventually forwarded to tty_interrupt, .ifi hit "K|tty_interrupt" .ifi hit "K|wakeup" which sends a wakeup to the relevant process to inform it of the change in the channel's state. The FNP is notified that the CS has processed the mailbox by means of an interrupt; if the interrupt is at the level corresponding to the mailbox number (8 to 11), .ifi hit "K|interrupt~level" it means that dn355 has updated the contents of the mailbox, and the FNP must read it back; an interrupt at a level 4 greater than the mailbox (i.e., 12 to 15) means that the mailbox is free for further use. .spb 2 There are three RCD operation codes that require a more extended transaction between the FNP and the CS: "send_output", "input_in_mailbox", and "accept_direct_input." These operation codes are used as "carriers" for reporting the amount of buffer space currently available in the FNP; this value is copied from the command data by dn355 and kept both for metering purposes and to determine how much output to send at a time. .ifi l3exact "send_output OPERATION CODE" .ifi l3toc "send_output Operation Code" .ifi hit "K|send_output~operation code" The "send_output" operation code is sent by the FNP when it is ready to accept output for a particular channel. When dn355 finds this operation code in a submailbox, it checks to see if there is any output for that channel waiting in tty_buf; if there is not, it forwards a send_output interrupt through channel_manager to tty_interrupt, which either calls back with any pending output that it did not send before, or wakes up the user process. If there is output waiting, dn355 prepares to send a WTX (write text) submailbox, as described later under "Transactions Initiated by the CS." .ifi l3exact "SHORT INPUT -- input_in_mailbox" .ifi l3toc "Short Input -- input_in_mailbox" .ifi hit "K|input_in_mailbox operation code" .ifi hit "K|free~space" .ifi hit "K|tty_buf" .ifi hit "K|reject request operation code" The "input_in_mailbox" operation code indicates that the FNP has 100 characters or less of input for the channel identified in the submailbox. The input itself is contained in the submailbox. If there is enough free space in tty_buf to copy this input into a buffer, dn355 allocates such a buffer, copies the data into it, and forwards an "accept_input" interrupt. It then sends the FNP an interrupt to free the submailbox. If buffer space cannot be obtained, dn355 rewrites the mailbox with a "reject request" operation code. In response to this code, the FNP retries the "input_in_mailbox" one second later. .ifi l3exact "LONG INPUT -- accept_direct_input" .ifi l3toc "Long Input -- accept_direct_input" .ifi hit "K|accept_direct_input operation code" .ifi hit "K|circular buffer" .ifi hit "K|input chain" .ifi hit "K|reject request operation code" The "accept_direct_input" operation code indicates that the FNP has more than 100 characters of input for the channel identified in the submailbox. This input will be written by the FNP into the circular buffer in the header of tty_buf. Accordingly, dn355 checks to see if there is sufficient space in the circular buffer for the specified number of characters; if there is not, or if it appears that there will not be enough space in tty_buf to build an input chain, dn355 rewrites the submailbox with the "reject request" operation code as above. .spb 2 .ifi hit "K|read text (RTX)" .ifi hit "K|input_accepted operation code" If there is sufficient space, dn355 fills in the submailbox with an RTX (read text) I/O command, an "input accepted" operation code, the absolute address pointed to by the circular buffer free pointer (tty_buf.cq_next), and the character count. If the data will not all fit between cq_next and the end of the circular buffer, the remaining data is put at the beginning of the buffer, and a "wraparound" address and character count are also provided in the submailbox. The free pointer (cq_next) is updated to point past the last character that will be input, the free size indicator (tty_buf.cq_free) is decremented accordingly, and the submailbox is sent to the FNP. .spb 2 .ifi hit "K|terminate interrupt multiplex word (timw)" .ifi hit "K|input chain" The FNP responds to the RTX submailbox by writing the input into the designated portion(@s) of the circular buffer and setting the TIMW bit for the submailbox so that dn355 will look at it again. When dn355 sees the RTX, it allocates a sufficient number of buffers to hold the input, and chains them to the channel's input chain (or starts an input chain if one does not already exist for the channel). Any space left at the end of the last buffer in an existing input chain is used before additional buffers are allocated. The data is then copied from the circular buffer into the input chain, cq_free is incremented by the amount of space thus freed in the circular buffer, an interrupt is sent to the FNP to free the submailbox, and an "accept_input" interrupt is forwarded as above. .brn 5 .ifi l3exact "HANDLING THE accept_input INTERRUPT" .ifi l3toc "Handling the accept_input Interrupt" .ifi hit "K|break character" .ifi hit "K|tty_interrupt" .ifi hit "K|wakeup" .ifi hit "K|formfeed" .ifi hit "K|output chain" .ifi hit "K|send_output~interrupt" If the input contains a break character (as indicated by a flag in the interrupt data) and the using process has tried unsuccessfully to read data from the channel (as indicated by wtcb.rflag), tty_interrupt sends a wakeup to the process so that it will call tty_read again and pick up the input. (A wakeup is also sent if the process is waiting for the terminal's answerback, as indicated by wtcb.wru.) An exception to this arises when the break character is a formfeed; in this case, dn355 does not forward an "accept_input" interrupt. If output to the channel had been suspended because of a full-page condition, dn355 checks to see if the PCB points to a pending output chain; if it does not, a "send_output" interrupt is forwarded; otherwise, an entry is added to dn355's delay queue .ifi hit "K|delay~queue" so that output processing will be resumed when dn355 finishes dealing with the input. (See also the description of the delay queue mechanism later in this section.) .ifi l2h "Transactions Initiated by the CS" .ifi hit "K|operation code (opcode)" .ifi hit "K|command data" .ifi hit "K|delay~queue" When tty_write or fnp_multiplexer wants to send output or control information respectively to the FNP, a call is made to fnp_multiplexer, which in turn calls dn355$send_wcd or dn355$send_wcd_global. The calling sequence of these entry points includes the operation code to be sent to the FNP and the associated command data, if any. When called at either of these entries, dn355 scans the "used" flags in the mailbox header looking for an unused submailbox. If all the submailboxes are currently in use, it calls the internal procedure make_q_entry to create an entry for the transaction in the delay queue, and returns; otherwise, it fills in the first free submailbox it finds and sends it to the FNP. .spb 2 .ifi hit "K|line~number" .ifi hit "K|device index (devx)" .ifi hit "K|write control data (WCD)" .ifi hit "K|terminate interrupt multiplex word (timw)" When passing control information, dn355 simply copies the operation code and command data into the submailbox, sets the line number in accordance with the device index supplied by the caller, sets the submailbox I/O command to WCD (write control data), and calls iom_manager to interrupt the FNP at the appropriate level. When the FNP has read the submailbox, it sets the corresponding TIMW bit and sends an interrupt; dn355$interrupt then sees the interrupt for a WCD submailbox and, knowing that the submailbox has been processed by the FNP, simply marks it as free (by turning off the corresponding "used" flag). More work has to be done, however, if the operation code is "accept direct output," indicating that there is output data to be sent to the FNP. The processing of output data by dn355 is described below. .ifi l3h "Output Data" .ifi hit "K|send_output~operation code" .ifi hit "K|accept_direct_output operation code" .ifi hit "K|output chain" .ifi hit "K|pseudo-DCW" The internal procedure process_send_output is invoked in response to either a "send_output" operation code sent from the FNP or a call to dn355$send_wcd with an "accept_direct_output" operation code. It ensures that there is in fact an output chain for the current channel, and that output to that channel has not been suspended because of a full-page condition; assuming both tests succeed, a block is allocated in tty_buf in which an array of "pseudo-DCWs" describing the output is built. Each pseudo-DCW contains the absolute address and character tally of one buffer in the output chain; the number of pseudo-DCWs constructed, and hence the number of buffers of output that are sent to the FNP in one transaction, depends on the amount of buffer space remaining in the FNP and the actual size of the chain, but in no case may exceed 16, which is the number of pseudo-DCWs that fit in one block. If dn355, while scanning the output chain, encounters a buffer with its end_of_page flag on, that buffer is the last one to be sent in this transaction, and pcb.end_frame is turned on so that the remainder of the chain (if any) will be processed when, and only when, a formfeed .ifi hit "K|formfeed" is input from the channel (see "Input Data," above). .spb 2 .ifi hit "K|write text (WTX)" .ifi hit "K|accept_direct_output operation code" .ifi hit "K|send_output~interrupt" .ifi hit "K|wakeup" Once the pseudo-DCWs have been built, the absolute address of the pseudo-DCW block and the number of pseudo-DCWs is put in a submailbox, along with a WTX (write text) I/O command and an "accept_direct_output" operation code (as a matter of NPS compatibility, the operation code is "accept_last_output" if the last buffer in the output chain is being sent, but the FNP makes no distinction between these two codes). This submailbox is then sent to the FNP. If the last buffer in the output chain is being sent in this transaction, and dn355 was entered through the interrupt entry, a "send_output" interrupt is sent to channel_manager. On receipt of this interrupt, tty_interrupt may send the next block of output if one is available, or send a wakeup to the user process so that tty_write can be called again. The forward pointer in the last buffer transmitted is set to zero so that any remaining buffers in the output chain will not be freed when the output completes (see below). .spb 2 When the FNP reads the submailbox containing the WTX, it reads the pseudo-DCW block and then uses the pseudo-DCWs to read the actual data into FNP memory. Once it has done this, it sets the TIMW bit corresponding to the submailbox; when dn355 examines the submailbox containing the WTX, it frees the portion of the output chain that was transmitted and the pseudo-DCW block. .ifi hit "K|terminate interrupt multiplex word (timw)" .ifi l3h "Global Operations" .ifi hit "K|global operations" A few of the control operations sent to the FNP do not refer to any particular channel but rather to the FNP itself: "accept_calls," "dont_accept_calls," "dump_fnp," and "patch_fnp." These are treated like other control operations, except that dn355 is called at the send_wcd_global entry rather than send_wcd. The line number field in the submailbox is accordingly set to zero. For dump_fnp and patch_fnp, .brn 5 used to dump and patch specified portions of FNP memory, respectively, fnp_multiplexer has called pxss$wait so that it can be informed when the I/O is complete; therefore, when the WCD submailbox is freed, if it contains either of these operation codes, dn355$interrupt calls pxss$notify. .ifi l1h "Locks" .ifi hit "K|channel~lock" .ifi hit "K|LCTE" .ifi hit "K|fnp_info" .ifi hit "K|physical channel block (PCB)" The channel lock in the LCTE for an FNP is used to protect not only the LCTE, but also the fnp_info structure, all PCBs, and the mailbox area for that FNP from simultaneous access by more than one processor. Because these data bases are accessed at interrupt time before channel_manager is invoked, dn355 and fnp_multiplexer must manage the lock themselves, instead of relying on tty_lock as described in Section!6. .ifi hit "K|tty_lock" (A flag in the LCTE informs channel_manager that it is not to call tty_lock for an FNP channel.) Accordingly, dn355$interrupt and most entries in fnp_multiplexer loop on the channel lock shortly after being called. The fnp_multiplexer entries must call pmut$wire_and_mask before doing this so as not to lose the processor with the FNP channel locked; they also check to see if they have been called as a result of an interrupt, in which case the channel has already been locked by dn355. .spb 2 .ifi hit "K|circular buffer" In addition, the circular buffer must be locked when the relevant pointers and free space indicators are being updated, to prevent two or more processors from attempting to modify it simultaneously on behalf of different FNPs. This situation is not covered by the fact that the circular buffer is never accessed without the FNP channel being locked because there is only one circular buffer for the whole system, rather than one per FNP. .ifi l1h "Delay Queuing" .ifi hit "K|delay~queue" .ifi hit "K|mailbox~operation code" If a mailbox transaction could not be performed when it was requested because no submailbox was available, an entry is added to a "delay queue" allocated in tty_buf so that the requested processing can be performed later. There is a separate delay queue for each FNP. An entry in this queue contains the offset of the PCB for the relevant channel, the mailbox operation code, and any associated command data. If the entry is for a global FNP operation, the PCB offset is 0. .spb 2 The dn355$interrupt entry checks to see if there are any entries in the current FNP's queue before and after examining the TIMW; .ifi hit "K|terminate interrupt multiplex word (timw)" if there are, it calls an internal procedure named process_q, which performs the operation requested by each queue entry as long as there are submailboxes available. .inl 0 .fin .brp  s5.mcs.compin 10/18/84 1051.8rew 12/09/81 1149.0 599373 .ifi init_plm "AN85-01" .srv section 5 .trf ! .ifi l0h "Interfaces to the User Ring" This section describes the hardcore portions of Multics Communication System that are called from outer rings in user processes. There are three modules that can be called thus: .spb .inl 10 .unl 5 1. tty_write .ifi hit "K|tty_write" .brf processes user-supplied output, copies it into tty_buf, and passes it on through dn355 to the FNP. It is called through the hcs_$tty_write and hphcs_$tty_write_force gate entries. .spb .unl 5 2. tty_read .ifi hit "K|tty_read" .brf converts terminal input from tty_buf and copies it into a user-supplied buffer. It is called through the hcs_$tty_read and hcs_$tty_get_line gate entries. .spb .unl 5 3. tty_index .ifi hit "K|tty_index" .brf performs a variety of user-requested utility functions, principally the processing of control operations. It is called at a variety of entry points through several gate entries, discussed later in this section. .inl 0 .spb 2 The precise calling sequences of these entry points can be ascertained by looking at the source code. In general, neither user nor system programs call these gate entries directly, but go through the I/O system (iox_) which calls entries in the user-ring typewriter I/O module (tty_). For details of this mechanism, see the U__s_e_r R__i_n_g I__n_p_u_t/_O__u_t_p_u_t PLM, Order No.!AN57. .ifi hit "K|I/O system (iox_)" .ifi hit "K|tty_" .spb 2 Both the tty_write and tty_read modules make use of an ALM subroutine tty_util_, which uses EIS instructions to do translations and searches of the input and output character strings. In addition, tty_read calls tty_canon to put input strings in canonical form (for a complete description of Multics canonical form, see the M_P_M_ C__o_m_m_u_n_i_c_a_t_i_o_n_s I__n_p_u_t/_O__u_t_p_u_t, Order No.!CC92). The tty_index module calls tty_modes to perform a "modes" control operation. All three modules use tty_tables to select translation and special-characters tables. .spb 2 .ifi hit "K|terminal control block (TCB)" The TCB contains relative pointers to the various tables used in the conversion of input and output. These pointers are set to zero when the channel is initialized, and subsequently set to default values by the "set_terminal_data" order. They may subsequently be changed as a result of control operations such as set_delays, set_output_translation, etc.; they are always restored to the default values when the "using" process is changed. The pointers used for output conversion by tty_write are copied into automatic storage. .ifi l1exact "O_U_T_P_U_T__C_O_N_V_E_R_S_I_O_N_ - tty_write" .ifi l1toc "Output Conversion - tty_write" .ifi hit "K|output conversion" .ifi hit "K|tty_write" .ifi hit "K|device index (devx)" .ifi hit "K|LCTE" .ifi hit "K|tty_index" The tty_write module takes user-supplied ASCII characters and converts them to a form suitable for printing on the user's terminal. Before it begins, it ensures that the device index passed to it is valid, that it refers to a terminal that is currently dialed up, and that the user process is actually using that terminal (i.e., wtcb.uproc contains the current process' id). It locks the LCTE to ensure that the terminal's state does not change out from under it (unless it was called at the tty_write$locked entry, which is called with the LCTE already locked by tty_index to write printer_on and printer_off sequences). .spb 2 The caller provides the count of characters that are to be processed ("nelem") and tty_write returns the number that were actually processed ("nelemt"). If nelemt is less than nelem, it is assumed that the caller will go blocked on the event channel named by wtcb.event; to signify that this is the case, tty_write turns wtcb.wflag on. On finding the flag on, tty_interrupt sends a wakeup over the event channel when a send_output interrupt comes from the FNP, as described in Section!4. .ifi hit "K|wakeup" .ifi hit "K|send_output~interrupt" .spb 2 The functions of tty_write can be logically divided into four phases: .spb .inl 10 .unl 5 .ifi hit "K|preliminary conversion" 1. P__r_e_l_i_m_i_n_a_r_y__c_o_n_v_e_r_s_i_o_n, as in the translation of lowercase letters to uppercase for a Teletype Model!33 or terminals in "capo" mode; .spb .unl 5 .ifi hit "K|formatting" .ifi hit "K|escape~sequence" 2. F__o_r_m_a_t_t_i_n_g, e.g., substitution of escape sequences, insertion of newline characters in long lines, canonicalization and optimization of white space, etc.; .spb .unl 5 .ifi hit "K|translation" 3. T__r_a_n_s_l_a_t_i_o_n, as from ASCII to EBCDIC; .spb .unl 5 .ifi hit "K|buffer~allocation and copying" .ifi hit "K|buffer" 4. B__u_f_f_e_r__a_l_l_o_c_a_t_i_o_n__a_n_d__c_o_p_y_i_n_g of characters into buffers in tty_buf, from which they are read by the FNP. .inl 0 .spb 2 Each phase is executed over the entire input string (or as much of it as is transmitted at once) before passing on to the next phase. In most cases, of course, either phase 1 or phase 3 or both can be omitted; in "rawo" mode, tty_write can and does proceed directly to phase 4. .ifi hit "K|tty_write" .spb 2 Each phase is provided with an "input pointer" to the location where the previous phase left the data in its latest form. This pointer points either to the user's original input or to either of two buffers in the automatic storage of tty_write, as described later. .spb 2 The rest of this section contains a more detailed description of the four phases of conversion mentioned above and a discussion of space allocation and character counting. .ifi l2h "Preliminary Conversion" .ifi hit "K|escape~character" Certain terminals require uppercase-only output; similarly, a user can specify (by entering "capo" mode) that all lowercase letters are to be converted to uppercase for output. These cases are treated identically by tty_write: an mvt (move with translation) instruction is used to copy the user's data into an automatic buffer, using a translation table that substitutes uppercase ASCII for lowercase. If the user is in "edited" mode, this is all that needs to be done for this phase; if not, however, each letter that was originally uppercase must be preceded by an escape character ("\"). Therefore, in "^edited" mode, the translation table also replaces each uppercase letter with the same character with its high-order bit (the "400(8)" bit) turned on. After the mvt is completed, an scm (scan with mask) instruction is executed to find the first character with the "400" bit on; if one is found, all characters to the left of it are copied to a second internal buffer, an escape is inserted after the copied characters, and the high-order bit of the found character is turned off. The scm is repeated on the remainder of the characters in the first buffer until all characters have been copied to the second buffer with escapes inserted as needed. If no characters with the high-order bit on are found in the entire string, no copying is done. .brp .ifi l2h "Formatting" .ifi hit "K|tty_write" .ifi hit "K|indicator" The search for, and correct handling of, "interesting" characters is the most crucial function of tty_write, and the one to which most of the time spent in tty_write is devoted. The identification of "interesting" characters is facilitated by the use of the tct (test character and translate) instruction under control of the output conversion table, which contains zero entries for all "uninteresting" characters and various indicators identifying the different kinds of "interesting" ones: carriage movement characters, ribbon shifts, and characters requiring the substitution of escape sequences. .spb 2 .ifi hit "K|escape~sequence" .ifi hit "K|newline" .ifi hit "K|vertical tab" .ifi hit "K|formfeed" .ifi hit "K|special_characters table" .ifi hit "K|delay~table" .ifi hit "K|white space" The formatting phase of tty_write calls tty_util_$find_char to find the first "interesting" character in the string; tty_util_$find_char returns a tally of "uninteresting" characters skipped over, the indicator value for the character it stopped at, and an updated pointer to the character at which to start the next scan. The tty_write module copies the uninteresting characters into an internal buffer (whichever one does not contain the source string) and examines the indicator. If it designates an escape sequence, the sequence is inserted in the buffer. For a newline, vertical tab, or formfeed character, the special_chars table is indexed to find the appropriate representation of the character, and the delay table is searched to find the correct number of delays to be inserted depending on column position, terminal type, and baud rate. For "white space" (horizontal tab, backspace, carriage return, or two or more blanks) tty_write simply calculates and remembers what column position to end up in; this information is either used to insert appropriate carriage motion characters before the next graphic to be inserted, or discarded if the next character involves vertical carriage motion. This process is repeated until all the source characters are used up. If it happens that the first call to tty_util_$find_char returns an indicator of zero and has used up the entire source string, no characters are moved by this phase. .spb 2 Another responsibility of the formatting phase is the counting of output lines and watching for full pages. When the line count reaches maximum, the formatting phase inserts a warning string (such as "EOP") and a sentinel character at the end of the page, and the copying phase (see below) later removes each sentinel and turns on a flag in the buffer that ends the page. When dn355 sees this flag, it ceases transmission, and sets pcb.end_frame. When it receives input for a channel with pcb.end_frame on, it scans this input for a formfeed; if it finds one, it turns off the flag and starts up output for the channel again. .ifi l2h "Translation" The translation phase is very similar to the preliminary conversion phase described earlier. An mvt instruction is used to copy the entire string from wherever it was left by the preceding phase to an automatic buffer, translating it from ASCII to the appropriate output code in the process. This does not complete the process for a terminal which requires case-shift characters (which currently includes most terminals for which translation is done); the insertion of case-shift characters is done in a similar manner to the insertion of escapes before capital letters as described under "Preliminary Conversion." The translation table causes the high-order bit of each uppercase character to be turned on (in this context, the term uppercase refers not only to capital letters but to all characters for which the shift key must be depressed while typing) and the "200(8)" bit of each lowercase character to be turned on; characters that may be in either case (such as space) contain no extra bits. After translation, an scm is done to find the first character in the opposite case to the one in which the terminal was at the start of the output; all characters to the left of it are copied, an appropriate shift character is inserted after the copied characters, and another scm is used to find the next change of case. If all the output characters are in the same case, no copying is done. Note that it is not necessary to turn off the high-order bits of the uppercase characters, since these bits are ignored by the remainder of Multics Communication System and ultimately thrown away by the FNP. .ifi l2h "Buffer Allocation and Copying" .ifi hit "K|tty_write" .ifi hit "K|buffer" .ifi hit "K|tty_buf" .ifi hit "K|buffer~size" .ifi hit "K|baud rate" The final phase of tty_write consists of allocating buffers in tty_buf and copying the final output into these buffers. The maximum buffer size for a channel is derived from its baud rate -- the faster the channel, the larger the buffers it gets. The size of buffer actually allocated for a given output message is the smallest multiple of 16 words in which the entire message will fit; if the entire message does not fit in the channel's maximum-size buffer, additional buffers are allocated and chained on to the first one. If an end-of-page sentinel is encountered, a flag is turned on in the current buffer, and the buffer is not filled past the sentinel. If output already processed for the particular channel has not yet been sent, a chain of buffers for that channel already exists, starting at the offset in wtcb.write_first; if the last buffer in this chain is not full, and does not have its end-of-page flag on, it is filled before further buffers are allocated. If the last buffer is less than the maximum size, it is replaced (if possible) by a larger buffer in which is placed the contents of the original buffer and as much of the new output will fit. The newly-allocated buffers are threaded onto the old chain. If wtcb.write_first is zero, tty_write starts a new chain. Finally, if wtcb.send_output is on, .ifi hit "K|parent multiplexer" indicating that the parent multiplexer is prepared to handle output for the channel, tty_write calls .ifi hit "K|tty_write" channel_manager$write to forward the output chain .ifi hit "K|output chain" (or as much of it as possible) to the multiplexer. .ifi l2h "Space Allocation and Character Counting" Because the input string undergoes wholesale modification at several points, it is necessary to decide how many of the user's characters to process before actually doing anything. Certain constraints are applied to keep any one channel from monopolizing tty_buf: no more than a certain fraction of available buffers in tty_buf are to be assigned to a single channel at any time; and no output chain of more than a certain number of buffers is built. The particular numbers involved are, for the sake of convenience and simplicity, preset system-wide constants. The current values are 1/4 and 16 respectively; i.e., no channel is ever assigned more than 1/4 as many buffers as are free at the time of assignment, or more than 16 buffers is a single output chain. .spb 2 This restriction does not apply when tty_write is entered at tty_write_force; in this case, the channel can have as many buffers as it needs as long as at least 32 words are left over. .spb 2 The number of characters to process may then be expressed as: .spb nchars!=!min(chars_supplied,!maxbuf*chars_per_buffer) .spb 2 If the terminal is in "rawo" mode, this is the number of characters that are actually shipped, and nothing further need be done. In general, however, the number of characters actually output is somewhat larger than the number supplied; meters done at various times show an average growth ratio of about 6:@5. Accordingly, for nonraw output, tty_write multiplies nchars as calculated above by 0.8 to allow for growth (this actually allows for a growth ratio of 5:@4, giving us some leeway). As a result, the size of the output string can grow by as much as 25% without requiring more buffers than one channel is "supposed" to have; however, the restriction to 1/4 of the available buffers is a very conservative one, so if it occasionally proves necessary to allocate an "extra" buffer, the overall effect on available buffer space should not be noticeable. .spb 2 An additional consideration arises from the use of internal buffers in tty_write. Because of the possibility of more than one intermediate copy, two such buffers are needed, and rather than create two segments so as to allow each buffer to grow essentially without limit, it was decided to set aside fixed-size buffers in the stack frame of tty_write. The size chosen for each of these buffers is the maximum allowable output chain size. .ifi hit "K|output chain" .spb 2 Clearly growth ratios greater than 5:@4 can and do occur; there are pathological cases such as an object or other non-ASCII segment being printed on a 2741 terminal, which involves a growth ratio of more than 6:@1 (!/c!!_n_n_n for each input character, plus added newlines and /cc markers). Thus, despite precautions, tty_write must be prepared for the possibility that in the course of translation or formatting it will run out of space in the internal buffer. When this happens, the number of input characters to be handled is cut in half, and character processing is started over from phase 1. .ifi hit "K|tty_write" .spb 2 If space in tty_buf is unusually tight, then an abnormal character string that is not large enough to overflow the internal buffer space might nonetheless require the allocation of more buffers than are available. If tty_write finds that it is about to allocate the last buffer, it takes the same action as if it were about to overflow one of its internal buffers, i.e., divide the number of input characters in half and start over. If this happens often, it is probably an indication that tty_buf is too small, and its size as defined on the PARM TTYB of the configuration deck card should be increased. .spb 2 If no buffers at all can be obtained, either because there are none available or because the channel already has as many as it is entitled to, no output is processed and zero is returned in nelemt. If, in addition, wtcb.send_output is on, tty_space_man$needs_space is called to ensure that the process receives a wakeup when more buffers become available (see the discussion of tty_space_man in Section!6). If wtcb.send_output is off, it is sufficient to turn on wtcb.wflag; this ensures that the process receives a wakeup when the multiplexer next requests output. .ifi hit "K|wakeup" .brf .ifi l1exact "I_N_P_U_T__C_O_N_V_E_R_S_I_O_N_ - tty_read" .ifi l1toc "Input Conversion - tty_read" .ifi hit "K|tty_read" .ifi hit "K|input chain" .ifi hit "K|get_chars operation" .ifi hit "K|get_line operation" .ifi hit "K|newline" .ifi hit "K|device index (devx)" .ifi hit "K|LCTE" The tty_read module takes typed input characters from the specified channel's input chain (pointed to by wtcb.fblock) and copies them, after suitable conversion, to a buffer supplied by the caller. There are two entries to tty_read that are normally called: tty_read itself and tty_read$tty_get_line. The former is called as a result of a get_chars operation, the latter as a result of a get_line operation. The difference is that the get_line entry only returns characters up to and including the first available newline character. Like tty_write, tty_read first validates the device index and ensures that it corresponds to an active channel, as well as locking the LCTE. Also like tty_write, it takes an input argument ("nelem") specifying the size of the caller's buffer and returns an output argument ("nelemt") specifying the actual number of characters copied into the caller's buffer. nelemt is the smallest of the following: .spb .inl 7 .unl 2 - nelem; .spb .unl 2 - the total number of characters (after conversion) in the read chain; .spb .unl 2 - if tty_get_line was called, the number of characters (after conversion) up to and including the first newline character in the read chain. .spb 2 .inl 0 If nelemt is zero, it is assumed that the caller will go blocked on the event channel whose name is in wtcb.event; tty_read accordingly turns wtcb.rflag on when returning zero in nelemt. When input containing a break character is copied into tty_buf by dn355 and forwarded to tty_interrupt (as described in Section!4), if wtcb.rflag is on, tty_interrupt sends a wakeup over the event channel so that the user process can call tty_read again. .ifi hit "K|break character" .ifi hit "K|wakeup" .spb 2 The break character recognized by tty_read is determined according to the line type associated with the channel. In general, the break character is a newline character. .ifi hit "K|break character" .ifi hit "K|tty_read" .ifi hit "K|line~type" .ifi hit "K|newline" .spb 2 Certain transformations may be performed on the characters typed by the user, such as reduction to canonical form, removal of "erased" and "killed" characters, and the interpretation of escape sequences. The application of these transformations depends on both the modes associated with the channel and the contents of the relevant tables in tty_tables. .ifi hit "K|canonical form" .ifi hit "K|escape~sequence" .ifi hit "K|modes" .spb 2 The functions of tty_read may be divided into the following phases: .spb .inl 10 .unl 5 1. C__o_p_y_i_n_g raw input data from tty_buf, and freeing the ring!0 buffers; .spb .unl 5 2. _T_r_a_n_s_l_a_t_i_o_n to ASCII .spb .unl 5 3. C__a_n_o_n_i_c_a_l_i_z_a_t_i_o_n of the contents of column positions .spb .unl 5 4. E__r_a_s_e__a_n_d__k_i_l_l__p_r_o_c_e_s_s_i_n_g .spb .unl 5 5. E__s_c_a_p_e__s_e_q_u_e_n_c_e__p_r_o_c_e_s_s_i_n_g .inl 0 .spb Clearly, these five phases are not always necessary. Phases 3, 4, and 5 depend on "can," "erkl," and "esc" modes, respectively; in "rawi" mode, only phase 1 is required. .spb 2 .ifi hit "K|conversion" For convenience and to ensure consistency, conversion (the generic term used here for the relevant subset of phases 2 through 5) is done on all characters up to and including the first break character in the input chain, whether or not the break character is found within the limit specified by .ifi hit "K|break character" the caller. This avoids the possibility of terminating conversion in the middle of an escape sequence .ifi hit "K|escape~sequence" or a line that is subsequently killed, and also allows for the possible shrinkage of the input string (through the deletion of extraneous white space and the condensation of escape sequences, for example). "Extra" characters thus converted (i.e., those that cannot be returned because the caller has not provided sufficient space) are saved in reallocated buffers in tty_buf; these buffers are marked by turning on buffer.converted and chained to the head of the channel's input chain so that they can be picked up by the next call to tty_read. In two exceptional cases, conversion cannot proceed to the first break character: the first is, obviously, when no break character is present; the other is when the size of the internal automatic buffers of tty_read is exceeded. .ifi hit "K|tty_read" .spb 2 The remainder of this discussion consists of a few remarks on the management of the internal buffer of tty_read and a more detailed description of the five conversion phases mentioned above. .brf .ifi l2h "Space Management" .ifi hit "K|space management" .ifi hit "K|break character" During conversion, intermediate forms of the input string result from each conversion phase; for the storage of these intermediate strings, two buffers are maintained in the automatic storage of tty_read. Clearly this sets an upper limit on the allowable length of the input string. The normal limiting factor, of course, is the presence of a break character, and input lines longer than 100 characters are rare; a further limitation is imposed by the FNP software, which takes a channel out of receive mode if more than 600 characters are input without a break character, causing "exhaust" status in the FNP (see Section!12). The input string can grow during canonicalization through the replacement of carriage returns by multiple backspaces, but this occurrence too is rare. All in all, a buffer size of 720 is very unlikely to be exceeded. .spb 2 .ifi hit "K|canonicalization" Consequently, no more than 720 characters are copied into the internal buffer from tty_buf. If the canonicalization phase attempts to increase the length of the string past 720, tty_read reduces the limit by one-third and starts again. Because of the possibility that this restart may be necessary, buffers in the read chain from which characters have been copied cannot be freed until after the canonicalization phase is completed. .spb 2 Since conversion is, if possible, carried out on all characters up to and including the first break character, the final converted string may be larger than the buffer provided by the caller. If this is the case, enough characters to fill the caller's buffer are returned; the remainder of the converted characters, as indicated above, are saved in buffers in tty_buf in each of which buffer.converted is set. In addition, if one of these buffers contains a break character (the last one generally does), buffer.break is turned on in that buffer. These buffers are added to the head of the input chain as described above. .brf .ifi l2h "Copying" .ifi hit "K|copying" .ifi l3exact "IN 'rawi' MODE" .ifi l3toc "In 'rawi' Mode" The copying phase in "rawi" mode is very simple. Characters are copied from tty_buf, starting at the head of the input chain, directly into the caller's buffer, until either the caller's buffer is filled or the input chain is exhausted. Any buffer from which all the characters are thus copied is freed. .brf .ifi l3exact "NOT IN 'rawi' MODE" .ifi l3toc "Not In 'rawi' Mode" If there are any "converted" buffers at the head of the input chain, characters are copied from these buffers directly into the caller's buffer until either the caller's buffer is full, a break character has been copied, or the chain of converted buffers is exhausted. (In general, the last converted buffer contains a break character, and nonlast converted buffers do not.) Any converted buffer from which all the characters are copied is freed. .ifi hit "K|break character" .spb 2 If there are no converted buffers, or the converted buffer chain is exhausted without encountering a break character or filling the caller's buffer, characters are copied from the unconverted input chain (if present) into the first automatic buffer of tty_read until either a break character is encountered, the input chain is exhausted, or the internal buffer is filled. Buffers are not freed at this time, for the reason given above under "Space Management." .spb 2 .ifi hit "K|break character" .ifi hit "K|quit" Because the FNP does not normally send input to the CS until a break character is typed, the input chain almost always ends with a break character. (Consequently, the converted chain usually does, too.) It might not if there was a quit on a channel not in "hndlquit" mode (in "hndlquit" mode the input chain is discarded on a quit), if the channel exceeded the 600-character limit enforced by the FNP software, or if the input is the answerback of the terminal. .spb 2 .ifi hit "K|conversion" If any characters were copied from unconverted buffers, conversion of the contents of the automatic buffer of tty_read begins. .brf .ifi l2h "Translation" .ifi hit "K|translation table" If a translation table exists for the channel, it is used in a call to tty_util_$mvt to copy the characters from one internal buffer to the other, simultaneously translating it to ASCII. Translation is required for IBM-type terminals using either EBCD or Correspondence character codes; it is also used to translate capital letters to lowercase for uppercase-only terminals such as a Teletype Model!33. (Escaped letters are changed back to uppercase by the escape-processing phase.) .spb 2 The translation phase does not have to deal with case-shift characters, since the FNP is responsible for recognizing case shifts and for turning on the 100(8) bit in all uppercase characters (characters on shifting terminals are only six bits). All that is necessary in the CS is a translation table that includes characters with the "100" bit on and translates case-shift characters to ASCII NUL characters. .spb 2 If the channel is not in "ctl_char" mode, a further translation is done using a general table that translates "invisible" characters to NUL (all zero) characters. NUL characters are subsequently discarded by the canonicalization phase. An "invisible" character is any ASCII control character that does not move the carriage or paper, i.e., one that cannot be seen when it is typed. This translation is omitted for terminals such as the IBM!Model!2741 and the IBM!Model!1050. .brf .ifi l2h "Canonicalization" .ifi hit "K|canonicalization" Column-position canonicalization takes care of itself unless the input string contains leftward carriage motion, i.e., backspace and/or carriage return characters. In addition, backspaces and carriage returns at the left margin or immediately preceding a newline are discarded. In other cases, canonicalization must be performed in accordance with the rules given in the MPM Communications I/O. .spb 2 The canonicalization phase therefore begins by searching the internal buffer (using the PL/I "search" builtin) for a left-motion character (carriage return or backspace). If the first character is a left-motion character, the buffer pointer is advanced by one character, the string length is decremented by one, and the new string is searched as before. If a left-motion character is found, a verify builtin is used to discover if the rest of the line consists of white space (backspaces, carriage returns, spaces, horizontal tabs, or NULs) followed by a newline. If this is the case, the string length is reduced to the result of the search, and the newline is copied to the new end of the string. If a left-motion character is discovered in any other position, tty_canon is called to perform .ifi hit "K|tty_canon" column canonicalization. .ifi hit "K|canonicalization" .spb 2 .ifi hit "K|tty_canon" The tty_canon subroutine applies the following algorithm: store each printing graphic from the input string in an array along with its correct column position; sort the array by column position, and by character within each column position; restore the characters to the input string location in the resulting order, inserting backspaces and spaces as appropriate. Tabs must be treated as a slightly special case of printing graphic, so that tabs that are in no way overstruck are preserved but others are replaced by spaces. .spb 2 The calling sequence of tty_canon has been set up so that the module could theoretically be called with an arbitrary string in other environments than that of ring!0 Multics Communication System. The resulting calling sequence is still not ideal, as it contains arguments that are both input and output; this approach is retained for reasons of efficiency. .spb 2 The structure used for the elements of the sorting array makes the sort very easy, thus: .spb .fif .all .inl 11 .unl 6 dcl 1 column_array (max_size) aligned, .brf 2 column fixed bin (17) unaligned, .brf 2 erase bit (1) unaligned, .brf 2 kill bit (1) unaligned, .brf 2 vertical bit (1) unaligned, .brf 2 pad bit (5) unaligned, .brf 2 not_tab bit (1) unaligned, .brf 2 char char (1) unaligned; .inl 0 .spb 2 .fin .alb The "erase" bit indicates an erase character; the "kill" bit indicates a kill character; the "vertical" bit indicates a nonnewline character requiring vertical carriage motion (i.e., vertical tab or formfeed); the "not_tab" bit is on for any character except a horizontal tab. It can be seen that by treating each element of the array as a single value for the purpose of sorting, the characters automatically come out in column order and in character order in each column, except that: 1) an erase character is always the last character in its column position; 2) a kill character is last in its column position unless overstruck with an erase character; 3) a horizontal tab is always the first character in its column position; and 4) a vertical-motion character follows all characters other than an erase or kill character. Since during the initial scan, a vertical-motion character causes both the "current" column and the "starting" column to be set to the next highest multiple of 1000 (the "starting" column is the column assigned to the left margin, initially 0), a vertical-motion character cannot share a column position unless 1000 or more column positions are actually typed. A newline is assigned a column position of 2**17!-!1 so that it always sorts to the end of the line. .spb 2 .ifi hit "K|kill processing" .ifi hit "K|kill character" .ifi hit "K|erase character" Kill processing is not done by tty_canon; kill characters are sorted to the end of the column position to make things easier for the kill-processing phase of tty_read. Erase characters are only interesting to tty_canon if they are overstruck; since an overstruck erase character sorts to the end of its column position, the rescan step, when it finds an erase character that is not first in its column position, deletes it and all preceding characters with the same column position. .spb 2 Since a tab sorts to the beginning of its starting column position, it is sufficient to check whether the graphic following the tab has a column position less than the next tab stop; if it does, the tab is dropped, and spaces are inserted as they are whenever there is gap between two graphics. Otherwise the tab is inserted in the final string. .spb 2 NUL characters are not stored in the column_array; thus tty_canon completes the elimination of "invisible" characters. .spb 2 The maximum length of the input string is passed as an argument to tty_canon; if the final string exceeds this length, only max_length characters are returned, and a status code of error_table_$long_record is returned. .spb 2 Upon return from tty_canon, if the status code is zero, tty_read frees the ring!0 buffers from which characters were copied, as explained above; otherwise it reduces its internal buffer size limit by one-third and starts again from the copying phase. .spb 2 If the canonicalization phase completes without calling tty_canon, the string may still contain NUL characters; therefore if tty_canon has not been called, tty_read indexes the string for NUL characters, and copies the characters preceding and following each NUL into the other internal buffer, decrementing the string length by one for each NUL it finds. .brf .ifi l2h "Erase and Kill Processing" .ifi hit "K|erase processing" .ifi hit "K|kill processing" .ifi hit "K|kill character" .ifi hit "K|escape~character" Erase and kill processing is really done in two passes, kill and then erase. The string resulting from the canonicalization phase is indexed from the right for a kill character; if one is found, and the immediately preceding character is not a nonoverstruck escape character, the pointer to the beginning of the string is incremented to point to the character following the kill character, and the length of the string is decremented accordingly. If the kill character is preceded by an escape character that is not preceded by a backspace, the pointer and the length are not changed, and the remainder of the string (if any) is scanned for further kill characters. .ifi hit "K|kill character" .ifi hit "K|escape~character" .spb 2 .ifi hit "K|erase character" .ifi hit "K|escape~character" The string resulting from the kill pass is now indexed for an erase character. If one is found anywhere but at the beginning of the string, the characters before and after the erased character(@s) must be copied to the other internal buffer. The basic mechanism is to copy the characters to the left of the erased characters, decrement the count of total input characters by the number of erased characters plus one for the erase itself, and resume the scan starting with the character after the erase character. (If the erase character is preceded by an escape character not preceded by a backspace, the escape and erase characters are copied along with the preceding characters.) When the end of the string is reached, provided any copying has been done, all characters to the right of the last erase character are copied. .spb 2 The number of characters to be erased (i.e., not copied) is determined as follows: if the character preceding the erase is "white space" (space or horizontal tab) the source string is searched backward for a nonwhite character, and all characters to the right of it are erased; if the character preceding the erase is a printing graphic, then the source string is searched backward until two nonbackspace characters are found in succession, whereupon all characters from the one to the left of the leftmost backspace on are erased. Note that the character immediately preceding the erase character cannot be a backspace, since all overstruck erase characters are processed by tty_canon. .spb 2 If the second or subsequent scan turns up an erase character as the first character in the string (as would happen if two erase characters were typed in succession), the determination of the number of erased characters is made in the same fashion as that described above, except that the characters at the end of the target string are examined; the erasing is carried out by decrementing the target pointer so that the erased characters are overwritten, and decrementing the overall length accordingly. .ifi l2h "Escape Sequence Processing" .ifi hit "K|escape~sequence" .ifi hit "K|break character" .ifi hit "K|input conversion table" This phase, which is implemented in a similar manner to the formatting phase of tty_write as described above, actually deals not only with escape sequences, but with the elimination of white space before break characters and of characters designated as being "thrown away" for the current terminal type. It uses test character and translate (tct) instructions under control of the input conversion table associated with the channel. .spb 2 .ifi hit "K|indicator" This phase uses tty_util_$tct, which scans for "interesting" characters and returns a tally of characters skipped over, the indicator value for the character stopped at, and an updated pointer to the character stopped at. If the tally is nonzero, tty_read copies the skipped characters into whichever internal buffer does not contain the source string; then it examines the indicator. For a break character, it scans the copied characters (if any) from the right for the last printing graphic; the break character is copied immediately to the right of it. If any intervening white space was found, the length of the final string is decremented by the number of white-space characters. Finally, a flag is set to indicate that a break was found. .spb 2 If the scan finds a formfeed, and the terminal has a nonzero page length, the formfeed is thrown away, on the assumption that the user typed it for the purpose of starting a new page. Otherwise it is stored as a normal character. The tty_interrupt module is responsible for adjusting the current line count on the page when a formfeed or newline is input. .spb 2 .ifi hit "K|indicator" .ifi hit "K|escape~character" .ifi hit "K|escape~sequence" .ifi hit "K|escape" .ifi hit "K|erase" .ifi hit "K|kill character" .ifi hit "K|special_chars" .ifi hit "K|indicator" If the indicator shows an escape character, tty_read must find out if it is in fact the start of an escape sequence. If the channel is not in "esc" mode, or if the character immediately preceding or either of the two characters immediately following the escape character is a backspace, the escape is copied as a normal character and the scan continues. (The backspace test is to ensure that neither the escape nor the column position to its immediate right is overstruck.) If the following character is an escape, erase, or kill character, it is copied to the target string; if it is an octal digit, the character whose value is represented by the one to three nonoverstruck octal digits following the escape character is inserted in the target string; if the escape is followed by zero or more white-space characters followed by a newline, all characters from the escape through the newline are skipped (the newline is not treated as a break in this case); otherwise the character following the escape is looked up in the input_escapes string in the appropriate special_chars structure (described in M_P_M_ C__o_m_m_u_n_i_c_a_t_i_o_n_s I__n_p_u_t/_O__u_t_p_u_t, Order No.!CC92). If it is found, the corresponding character from the input_results string is inserted in the target string. If the character is not found, then there is no escape sequence, and the escape character is copied as above. If an escape sequence is identified, the pointer used for the next call to tty_util_$tct is updated to point past the end of the escape sequence. .spb 2 If the indicator shows that the character is to be thrown away, it is not counted in the length of the final string, and the scan continues starting with the following character. Note that "invisible" characters (see above) have already been thrown away by the time this phase is reached. If the first call to tty_util_$tct returns an indicator of zero and uses up the entire source string, no characters at all are copied by this phase. .spb 2 .ifi hit "K|break character" If the total number of characters in the now fully-converted string plus the number of previously-converted characters already copied into the caller's buffer is less than or equal to the number of characters requested by the caller, and the converted string ends in a break character, all the converted characters are copied into the caller's buffer, and tty_read returns. If the total number of converted characters exceeds the number requested by the caller, the caller's maximum is copied into the caller's buffer, and the remainder are placed in "converted" buffers in tty_buf as described above, to be picked up by a future call. If the total number of converted characters is less than the number requested by the caller, and the converted string does not end in a break character (either because a break character was escaped, or because the internal buffer size limit was reached), all available characters are copied to the caller's buffer and, if an input chain is still present, the next block of characters (up to the next break) is copied from the input chain and converted as above; any excess characters resulting from the latter conversion are saved in "converted" buffers as above. .ifi l2h "Echo Negotiation" .ifi hit "K|echo negotiation" .ifi hit "K|breakall mode" .ifi hit "K|tty_interrupt" .ifi hit "K|wakeup" A special method of input processing is available to users of breakall mode, intended for use with the experimental emacs editor. This feature is called "echo negotiation"; when it is in effect, although an interrupt is generated by every input character, tty_interrupt only sends wakeups when one of a list of designated characters is encountered. Characters that do not cause wakeups are "echoed" by tty_interrupt, i.e., they are returned to the FNP as output. .spb 2 .ifi hit "K|tty_read" The feature is implemented by a combination of a control operation to establish the characters that generate a wakeup, and an entry to tty_read to start echoing. The "set_echo_break_table" order establishes a table of characters in ring 0; once the table has been established, the entry tty_read$echo_negotiate_get_chars may be called to initiate echo negotiation. This entry does everything that the main tty_read entry does; in addition, if it is called at a time when no input for the channel is present in ring 0, it sets a flag in the WTCB. When input arrives and this flag is on, tty_interrupt looks up each character in the table, and either echoes it or wakes up the process. .ifi hit "K|echo negotiation" Echo negotiation is turned off by any of the following events: a call to either of the two normal tty_read entries; a call to the echo_negotiate_get_chars entry that results in characters being returned (in which case a count of the number of characters that have been echoed is also returned); the arrival of a character in the echo break table; or the accumulation of a specified number of characters (which is set by the call to the echo_negotiate_get_chars entry). .ifi l1exact "U_T_I_L_I_T_Y__F_U_N_C_T_I_O_N_S_ - tty_index" .ifi l1toc "Utility Functions - tty_index" .ifi hit "K|tty_index" .ifi hit "K|tty_read" .ifi hit "K|tty_write" .ifi hit "K|terminal control block (TCB)" The tty_index module has several entry points that can be called through gates to perform various operations on a nonmultiplexed communications channel on behalf of an outer ring. These functions include initializing, attaching and detaching of channels, passing "ownership" of channels between processes, the implementation of control operations, etc. One other entry, tty_index$initialize_tcb, is called by tty_read and tty_write if they are called before the TCB is initialized. The remainder of this section describes the actions of the different entries. .ifi l2h "Initializing a Channel" .ifi hit "K|nonmultiplexed channel" The init_channel entry is called by priv_channel_manager$init_channel (see Section!3) to initialize a nonmultiplexed channel. It simply allocates a WTCB in tty_buf and a TCB in tty_area and returns a pointer to the WTCB for priv_channel_manager to store in the channel's LCTE. .ifi l2h "Terminating a Channel" The terminate_channel entry is similarly called by priv_channel_manager$terminate_channel when a channel's parent multiplexer crashes or hangs up. Its function is to free the space occupied by the TCB and the WTCB. .ifi l2exact "A__s_s_i_g_n_i_n_g__a_C__h_a_n_n_e_l__t_o__a_P__r_o_c_e_s_s" .ifi hit "K|tty_attach" .ifi hit "K|channel~name" .ifi hit "K|device index (devx)" .ifi l2toc "Assigning a Channel to a Process" The two entries, tty_index and tty_attach, given a channel name, return the device index and the current state of the channel to an outer-ring caller. They are called through the gate entries hcs_$tty_index and hcs_$tty_attach. The device index can then be used in subsequent calls to tty_read, tty_write, and other entries in tty_index. If appropriate, they also make the current process either the "owning" process of the channel (wtcb.hproc) or the "using" process (wtcb.uproc). The two entry points behave in the same way except that tty_attach sets an event channel name in the WTCB, as described later. In effect, a call to tty_attach is equivalent to a call to tty_index followed by a call to tty_event (see below); the following description of the actions of tty_index applies to tty_attach as well. .spb 2 .ifi hit "K|tty_index" .ifi hit "K|logical channel name table (LCNT)" .ifi hit "K|LCTE" .ifi hit "K|device index (devx)" .ifi hit "K|conversion~table" The first thing tty_index does is to look up the channel name in the LCNT, in order to obtain the device index. It uses this to get a pointer to the LCTE of the channel, which it locks. At this point, if no process has previously been assigned ownership of the channel and the calling process is the initializer, the calling process is made the owning process. If wtcb.hproc is not zero, the calling process' id must already be in either wtcb.hproc or wtcb.uproc; if it is not, an error code is returned and nothing is done. If the caller is entitled to alter the state of the channel (as determined by the above test), the caller's process id is placed in wtcb.uproc (it may have been there already, or the answering service may have taken control of the channel in preparation for creating a new process). If wtcb.uproc is changed by this action, the pointers (in the TCB) to the conversion tables are set to default values. The state of the channel (listening, dialed, or neither) is set according to the flags in the WTCB; then tty_index returns (or, if the tty_attach entry was called, it joins the code for tty_event, described below). .spb 2 .ifi hit "K|tty_event" .ifi hit "K|event channel" .ifi hit "K|wakeup" .ifi hit "K|break character" All entries that deal with a specific channel, other than init_channel, terminate_channel, tty_index, and tty_attach, call an internal procedure, setup, that ensures that the device index supplied by the caller is valid, locks the LCTE, gets pointers to the WTCB and the TCB, and checks to see whether the caller is entitled to access to the channel (either the channel is unowned or the caller is either the owner or the user of the channel). It also sets the state of the channel, which is returned to the caller. .ifi l2toc "Assigning an Event Channel" .ifi l2exact "A__s_s_i_g_n_i_n_g__a_n_E__v_e_n_t_C__h_a_n_n_e_l" The tty_event entry is called through the hcs_$tty_event gate entry to record the name of the event channel over which Multics Communication System-originated wakeups are to be sent. After the usual validation, the event channel name supplied by the caller is placed in wtcb.event; this event channel is then used for all output completions, break character inputs, and similar events. If the caller is the owning process, the event channel name is also placed in wtcb.hevent, and is used to signal dialups and hangups as well. .ifi l2exact "S__e_p_a_r_a_t_i_n_g__a_C__h_a_n_n_e_l__f_r_o_m__a_P__r_o_c_e_s_s" .ifi l2toc "Separating a Channel from a Process" Two entries are used to break the connection between a process and a communications channel: tty_detach and new_proc. The tty_detach entry, called through hcs_$tty_detach, has two uses, depending on the value of its second argument (dflag): if dflag is zero, the caller is presumably the owner, in which case, the channel is taken away from the current user (i.e., wtcb.uproc is set to zero)--if the user calls this entry, nothing is done; if dflag is nonzero, the caller must be the owner, who is making the call in order to give up ownership of the channel. In this latter case, tty_detach calls channel_manager$control to hang the line up and sets wtcb.hproc to zero. As far as is known, the initializer never calls this entry with dflag nonzero. .spb 2 The new_proc entry is called through hcs_$tty_new_proc by the "owning" process in order to change the "using" process. If the channel is not dialed, nothing is done; otherwise, the value of the second argument (nproc) replaces the old contents of wtcb.uproc. This entry is called by the answering service after it is woken up by a user's new_proc command. .ifi l2exact "A__s_c_e_r_t_a_i_n_i_n_g__t_h_e_S__t_a_t_e__o_f__a_C__h_a_n_n_e_l" .ifi l2toc "Ascertaining the State of a Channel" .ifi hit "K|tty_state" The tty_state entry is called through hcs_$tty_state in order to find out if a channel is dialed, listening, or neither. It returns the appropriate code based on the values of flags in the WTCB. .ifi l2exact "A__b_o_r_t_i_n_g_I__n_p_u_t__a_n_d/__o_r_O__u_t_p_u_t" .ifi l2toc "Aborting Input and/or Output" The tty_abort entry is called through hcs_$tty_abort as a result of a "resetread" or "resetwrite" control operation. The second argument indicates whether input or output is to be aborted; 1 means input, 2 means output, 3 means both. The general procedure is the same in either case: the input or output chain is freed and the head and tail .ifi hit "K|input chain" .ifi hit "K|output chain" pointers in the WTCB are zeroed. The "abort" order is forwarded through channel_manager$control to cause any pending input or output being held at higher levels of multiplexing or in the FNP itself to be discarded. .ifi l2exact "C__o_n_t_r_o_l_O__p_e_r_a_t_i_o_n_s" .ifi l2toc "Control Operations" .ifi hit "K|tty_index" .ifi hit "K|control operation" .ifi hit "K|modes operation" .ifi hit "K|major channel" .ifi hit "K|multiplexer~module" The rest of tty_index--which is to say about two-thirds of it--is devoted to the implementation of the various control operations available through the typewriter DIM. The tty_order entry, called through hcs_$tty_order, implements all the control operations available to users through iox_ and tty_ (except for resetread, resetwrite, and abort, which are implemented by tty_abort, above) as well as the modes operation. In general, these operations have to be forwarded to the major channel through channel_manager$control, in case action by the multiplexer module is required to implement the operation. .spb 2 The effect of the control operations implemented in tty_index is described in the description of the tty_ I/O module in the M_P_M_ C__o_m_m_u_n_i_c_a_t_i_o_n_s I__n_p_u_t/_O__u_t_p_u_t, Order!No.!CC92. Rather than describe the implementation of each control operation in detail, the rest of this section discusses special actions of a nonobvious nature which must be taken in connection with certain operations. .ifi l3exact "read_status OPERATION" .ifi l3toc "read_status Operation" .ifi hit "K|read_status operation" .ifi hit "K|input chain" .ifi hit "K|tty_read" This operation is used by outer-ring programs that wish to find out the state of the input chain without necessarily going blocked. Since all the characters in an input buffer are not necessarily processed by a single call to tty_read, tty_order must check, if there is exactly one buffer in the read chain, whether it contains characters that tty_read has not processed; hence, the buffer tally is compared against wtcb.fchar (which is the offset of the first unprocessed character position in the buffer), and if they are equal, tty_order returns the information that there is no input available in tty_buf. Whenever this result is returned (it is also returned if wtcb.fblock is zero, of course), wtcb.rflag is turned on so that if the caller chooses to go blocked for input later a wakeup is sent when the input arrives. .ifi l3exact "write_status OPERATION" .ifi l3toc "write_status Operation" .ifi hit "K|write_status operation" .ifi hit "K|output chain" If this operation reports that output is going on, wtcb.wflag must be turned on so that if the caller goes blocked for output later it can be woken up when the current output chain is sent. .ifi l3exact "printer_off OPERATION" .ifi l3toc "printer_off Operation" .ifi hit "K|printer_off operation" This operation is intended to prevent typed input from appearing on the terminal, for example when reading passwords. Since echoplex and replay modes both involve input being printed on the terminal by the FNP, special handling is required if an outer-ring program invokes the printer_off operation while the channel is in either of these two modes. .spb 2 .ifi hit "K|printer_off sequence" .ifi hit "K|special_characters table" The normal method for turning the printer off is to send the printer-off sequence defined in the special-characters table for the current terminal type as output. This is done by temporarily placing the channel in rawo mode, calling tty_write$locked so that tty_write does not attempt to lock the LCTE (since tty_index has already done so), and restoring the previous setting of rawo mode. If the special-characters table for the current terminal type does not contain a printer-off sequence, no output is sent and a status code of error_table_$action_not_performed is returned. .spb 2 If the channel is in echoplex mode, disabling the terminal's local-copy function would be ineffective (and has probably already been done). In this case, tty_order sends a modes operation to channel_manager to take the channel out of echoplex mode; the mode bits in the TCB, however, are not changed so that echoplex is automatically restored if a printer_on control operation is requested later. .spb 2 .ifi hit "K|tty_order" If the channel is in replay mode, tty_order must tell the FNP to take it out of replay mode when turning the printer off in response to an outer-ring request. As with echoplex mode, when the printer_off operation does take the channel out of replay mode, it leaves tcb.replay on so that replay is restored by a subsequent printer_on operation. .spb 2 The actual output operation to turn the printer off is effected by an internal procedure, which is also called to disable local copying when the channel is put in echoplex mode (see discussion of "Modes", below). .ifi l3exact "printer_on OPERATION" .ifi l3toc "printer_on Operation" .ifi hit "K|printer_on operation" The printer_on operation is used to undo the effect of a previous printer_off operation. The same considerations apply: if the channel is in echoplex mode, rather than enabling the local-copy function, tty_order tells the FNP to start echoplexing; if the channel is in replay mode, the FNP is told to resume replaying. For more information, see the discussion of echoplex mode under "Modes," below. .ifi l3exact "set_terminal_data OPERATION" .ifi l3toc "set_terminal_data Operation" .ifi hit "K|set_terminal_data operation" .ifi hit "K|terminal type table (TTT)" The set_terminal_data operation is used by tty_ to implement the set_term_type order. It extracts information from the terminal type table (TTT) entry for the requested terminal type, and stores it in the structure defined in the include file terminal_type_data.incl.pl1; this structure is passed to tty_order with the set_terminal_data operation. It includes pointers to the default translation, conversion, special, and delay tables for the terminal type; these tables are copied into tty_tables by calls to tty_tables_mgr, and pointers to the copies are set in the TCB. The default table pointers in the TCB are set to -1 to indicate that the default tables are in effect. .ifi hit "K|translation table" .ifi hit "K|conversion~table" .ifi hit "K|special table" .ifi hit "K|delay~table" .ifi hit "K|tty_order" .ifi hit "K|tty_write" .spb 2 The setting of a terminal type by tty_ may include, besides passing the set_terminal_data operation to tty_order, passing in the initial modes for the terminal type and writing the initial string to the terminal. These operations are accomplished by separate calls to tty_order and tty_write, respectively. .ifi l3exact "wru OPERATION" .ifi l3toc "wru Operation" .ifi hit "K|wru operation" .ifi hit "K|break character" The wru ("who-are-you") operation, used to initiate a read of the terminal's answerback, is available only to the owning process. The wtcb.wru flag is turned on so that a wakeup is sent to the requesting process when the FNP returns the answerback sequence even if the sequence does not contain a break character. .ifi l3h "Modes" .ifi hit "K|modes operation" The modes operation is implemented in four phases: first, the caller-supplied character string is validated, and bit strings are set up indicating which modes are to be turned on and off; second, the modes are forwarded to the parent multiplexer, in case any action at the next level of multiplexing is required; third, the appropriate mode bits in the TCB are turned on and off, and any other special actions required for each particular mode are taken; finally, the previous settings of tcb.modes are converted into a character string to be returned to the caller. If any error is discovered during the first phase, no modes are changed, and an error code is returned. The modes operation is implemented in a separated module named tty_modes, which is called by tty_order. .spb 2 Validation consists primarily of ensuring that all the mode names supplied by the caller are actually names of modes recognized by Multics Communication System. This includes calling channel_manager$check_modes to see if any of the modes are recognized by the multiplexer module. In a few cases, however, further checking must be done. For line length and page length, the mode name ("ll" or "pl") must be followed by an integer that is either zero or in the range of 5<=n<=255. For "fulldpx" (full duplex) or any of the echoing modes (crecho, lfecho, tabecho, or echoplex) the channel must be capable of operating in full-duplex mode; this is determined by looking up the line type (wtcb.line_type) in a table of line types capable of running in full duplex. (This test is performed by fnp_multiplexer$check_modes.) In addition, for crecho, lfecho, and echoplex modes, the FNP has to supply padding when echoing carriage motion characters; tty_modes, therefore, forwards the channel's current delay table to the FNP. .spb 2 The actual setting of a mode by tty_modes entails turning on or off the corresponding bit in tcb.modes. For most modes, this is all that has to be done; these are modes that affect the operation of the hardcore portion of Multics Communication System only, and not the FNP. For other modes, it is necessary to send an alter_parameters operation to the FNP (see Appendix!A for a description of the alter_parameters operation) telling it to turn the specified mode on or off. This is all that needs to be done for tabecho, hndlquit, replay, and polite modes. Echoplex mode is more complicated: when turning it on, not only is the delay table forwarded, but a printer_off operation is requested to disable local-copy (see the discussion of the printer_off operation, above); similarly, when turning echoplex off, a printer_on operation is requested to restore local copy. .ifi hit "K|printer_off operation" .spb 2 Line length and page length are not recorded in tcb.modes at all, but are reflected in tcb.colmax and tcb.linemax respectively. A zero value for either of these lengths means that checking for maximum line or page length is suppressed. .ifi l2h "Privileged Operations" Privileged operations may be defined for a multiplexer; these are invoked through the gates phcs_$tty_control and hphcs_$tty_control and forwarded through priv_channel_manager$priv_control and priv_channel_manager$hpriv_control, respectively. The ones described below are those defined for an FNP channel; they are inplemented by the fnp_multiplexer module. The dump_fnp operation is implemented by the priv_control entry; the others are implemented by the hpriv_control entry. .ifi l3exact "dump_fnp Operation" .ifi l3toc "dump_fnp Operation" .ifi hit "K|dump_fnp operation" In order to obtain the contents of a specified portion of FNP memory, it is necessary to supply a wired buffer for the FNP to write into; accordingly, a sufficient amount of space is allocated in tty_buf. The fnp_info structure contains a lock used to prevent more than one dump_fnp or patch_fnp operation from using the facility simultaneously and a dump_patch_in_progress flag to indicate that such an operation is currently going on. After fnp_multiplexer calls dn355$send_wcd to tell the FNP to dump the specified memory locations into the wired buffer, it waits (using the wait/notify mechanism of pxss) until the DIA I/O is complete. When dn355 receives an interrupt for the submailbox used for the dump_fnp operation (see Section!4 for a more detailed description of this mechanism) it sends a notify for the event reserved for use by Multics Communication System and turns off the dump_patch_in_progress flag. When fnp_multiplexer receives the notify and sees that the flag is off, it copies the data from tty_buf into the buffer supplied by the caller, frees the wired buffer, and unlocks the lock. .ifi l3exact "patch_fnp Operation" .ifi l3toc "patch_fnp Operation" .ifi hit "K|patch_fnp operation" This operation allocates space in tty_buf and uses the "dump_patch_lock" in fnp_info in the same manner as the dump_fnp operation, aside from the essential and obvious difference that caller-supplied data is placed in the wired buffer by fnp_multiplexer and sent to the FNP. The wait/notify mechanism is also used in the same way. .spb 2 The patch_fnp operation assumes that the caller has previously done a dump_fnp operation, the results of which must be supplied with the patch_fnp call so that the old and new contents of each word of FNP memory being patched can be reported on the operator console and in the syserr log. .ifi l3exact "fnp_break Operation" .ifi l3toc "fnp_break Operation" .ifi hit "K|fnp_break operation" .ifi hit "K|breakpoint" .ifi hit "K|control table" .ifi hit "K|debug_fnp command" This operation is used to manage breakpoints in FNP control tables. (See the discussion of control tables in Section!12 and the description of the debug_fnp command in Appendix!B.) The data structure includes an FNP address, an optional channel name, and the action to be performed by the FNP; this action may be to set a breakpoint, to restart a channel stopped at a breakpoint, or to reset a breakpoint. If no channel name is specified, the action is taken to apply to all subchannels of that FNP. The contents of the structure are passed (with appropriate modifications) to dn355$send_global_wcd, which passes them to the FNP in a submailbox with an operation code of "fnp_break". .ifi l3exact "enable_breakall_mode Operation" .ifi l3toc "enable_breakall_mode Operation" .ifi hit "K|enable_breakall_mode operation" .ifi hit "K|breakall mode" This operation must be performed before breakall mode can be used on subchannels of the FNP. A channel name is passed with the control operation; it may be either the name of the FNP channel itself or the name of one of its subchannels. If it is the name of the FNP channel, the mode is enabled for all channels on that FNP, and a flag is set in the fnp_info structure. If the name specified is that of a subchannel, the mode is enabled for that subchannel only, and a flag is turned on in its PCB. For breakall mode to be turned on for a channel, either its PCB flag or the FNP's global flag must be on. .ifi l3exact "disable_breakall_mode Operation" .ifi l3toc "disable_breakall_mode Operation" .ifi hit "K|disable_breakall_mode operation" This operation undoes the effect of an earlier enable_breakall_mode operation by turning off the specified flag. Note that if an FNP channel name is specified, no PCB flags are turned off; thus a disable_breakall_mode operation for an entire FNP does not affect subchannels for which breakall mode has been enabled explicitly. .inl 0 .fin .brp  s6.mcs.compin 10/18/84 1051.8rew 12/09/81 1149.0 116181 .ifi init_plm "AN85-01" .srv section 6 .ifi l0h "Hardcore Utilities" .ifi hit "K|tty_lock" .ifi hit "K|tty_space_man" .ifi hit "K|tty_util_" .ifi hit "K|dn355_util" This section describes the operation of two utilities widely used by ring!0 Multics Communication System, tty_lock and tty_space_man; and two special-purpose assembler language subroutines, tty_util_ and dn355_util. .ifi l1h "Locking and Queuing" .ifi hit "K|locking" .ifi hit "K|channel~lock" .ifi hit "K|LCTE" The program that manages the "channel lock" in each LCTE, used to prevent access to either the LCTE or any of the associated channel's databases when their state is potentially inconsistent, is tty_lock. The following entries are provided: lock_channel and unlock_channel, which lock and unlock LCTEs at call time; lock_channel_int and unlock_channel_int, which lock and unlock LCTEs at interrupt time; flush_queue, which is used to clean out pending queue entries when a channel is terminated; and verify, which is called by verify_lock when a crawlout occurs, to make sure no process leaves ring 0 with a processor lock locked. Because some of these entries are called at interrupt time, tty_lock must be wired. .spb 2 .ifi hit "K|lock_channel entry" .ifi hit "K|channel_manager" .ifi hit "K|tty_read" .ifi hit "K|tty_write" .ifi hit "K|tty_index" The lock_channel entry is called by channel_manager, tty_read, tty_write, and tty_index before any references to an LCTE. (dn355 does its own LCTE locking, as described in Section!4.) It uses a stac instruction to attempt to lock the LCTE; if the lock is already locked to some other process, the stac fails, and a wait/notify mechanism is used to determine when the lock becomes unlocked. The notify_reqd flag in the LCTE is turned on; lock_channel calls pxss$addevent to establish an event associated with the lock, tries once more to lock the lock (in case the other process has unlocked it in the interim), and, if it still fails, calls pxss$wait. The process now waits until a notify is sent for the associated event; when this happens, lock_channel tries again to lock the lock, and, if it still cannot do so, reestablishes the event and waits again. .spb 2 .ifi hit "K|lock_channel_int entry" The lock_channel_int entry is called by channel_manager$interrupt. Since pxss$wait must not be called at interrupt time, this entry does not wait for the lock if it is already locked; instead, it adds an entry to the channel's delay queue. This queue entry contains the interrupt type and associated data. Any queue entries added in this way are processed when the channel is unlocked, as described later in this section. .ifi hit "K|delay~queue" .spb 2 If lock_channel_int succeeds in locking the channel, it sets a locked_for_interrupt flag in the LCTE. The reason for this is that the interrupt handler may call one of the other entries in channel_manager in order to pass information back to the multiplexer as a result of the interrupt. In this case, lock_channel would find the channel already locked; the setting of the locked_for_interrupt flag would indicate that it was all right for the call to proceed without waiting for the lock to be unlocked, since the operation channel_manager was called to perform is part of the same transaction that locked the channel in the first place. .spb 2 The unlock_channel and unlock_channel_int entries are almost exactly alike, except for their treatment of the locked_for_interrupt flag. The unlock_channel_int entry turns the flag off before unlocking the channel; the unlock_channel entry does not unlock the channel at all if the flag is on, since the unlock_channel_int entry is due to be called later. .spb 2 .ifi hit "K|delay~queue" Before unlocking the channel, either unlock entry processes the channel's delay queue: for each entry in the queue, it calls channel_manager$queued_interrupt (which differs from channel_manager$interrupt only in that it does not attempt to lock and unlock the channel) and frees the queue entry. When no more entries remain in the queue, the lock is unlocked. .spb 2 .ifi hit "K|queue lock" A separate lock known as the queue lock is used to protect the delay queues from simultaneous modification. This lock is also used to protect changes in the state of a channel lock that could require referencing a queue; i.e., lock_channel_int never locks a channel lock, and no one ever unlocks one, without first locking the queue lock. This strategy is modeled on that used for the coreadd queue lock, which is described in detail in the M__u_l_t_i_c_s!S__t_o_r_a_g_e!S__y_s_t_e_m!P_L_M_, O__r_d_e_r!N__o.!A_N_6_1_. .spb 2 Once a channel lock is unlocked, if notify_reqd is on, either unlock entry calls pxss$notify so that the process that is waiting in lock_channel can try once again to lock the lock. .spb 2 .ifi hit "K|crawlout" .ifi hit "K|tty_buf~lock" The verify entry is called by verify_lock at crawlout time. It checks to see if the global tty_buf lock, tty_buf.slock, is locked to the current process; if it is, cleanup_locks crashes the system, since the state of tty_buf itself must be assumed to be inconsistent. Similarly, the system crashes if queue lock is locked to the current process, or any channel lock for a channel whose "special lock" flag is on, indicating that the channel's multiplexer (rather than tty_lock) manages the lock. .ifi hit "K|queue lock" .ifi hit "K|channel~lock" .brf .ifi l1h "Space Management" .ifi hit "K|space management" .ifi hit "K|tty_buf" Wired buffer space in tty_buf is managed by entries in tty_space_man. While manipulating the buffer pool, these entries must have the global or "system" lock, tty_buf.slock, locked; to avoid losing the processor while this lock is locked, they must run wired and masked. Three kinds of action are performed by tty_space_man: allocation, freeing, and the setting of "space_needed" flags. The formats of the blocks manipulated by tty_space_man are described in Section!2. .ifi l2h "Allocation" .ifi hit "K|space allocation" .ifi hit "K|buffer~chain" .ifi hit "K|size code" Space in tty_buf may be allocated either for use in buffer chains (for input and output data) or for any of the various control structures described in  Section 2. Buffers are allocated by the entries get_buffer and get_chain; other control blocks are allocated by the entry get_space. The principal difference is that the size of a buffer is always a multiple of 16 words, with a maximum size of 128 words; get_space, on the other hand, can allocate space in any even number of words. The buffer allocation entries set the size code in each allocated buffer (the meaning of the size code is explained in the description of data buffers in Section 2). .spb 2 .ifi hit "K|get_buffer entry" .ifi hit "K|LCTE" The get_buffer entry is called to allocate a single buffer whose size is a multiple of 16 words; get_chain is called to allocate a chain of such buffers, all of equal size, each containing a relative pointer to the next one in the chain; get_space allocates a block of arbitrary size. In any case, tty_space_man searches the chain of free blocks from the lowest address, looking for the smallest free block that is large enough to contain a buffer or block of the requested size. If a chain is being allocated, this process is repeated for each requested buffer, and each buffer allocated (except the last one) has its "next" pointer set to the offset of the following buffer. The buffer allocation entries also update a field in the LCTE of the channel on whose behalf they were called, to indicate how much input and output space is currently assigned to that channel. This information is used by dn355 and tty_write to determine how much input and output, respectively, to accept for the channel. The contents of a buffer or block are set to zero upon allocation, except for the size code and forward pointer if appropriate. If tty_space_man is unable to find a block or blocks of the requested size, it returns a null pointer; it is the responsibility of the caller to take appropriate action. .ifi l2h "Freeing" .ifi hit "K|buffer" .ifi hit "K|buffer~chain" The free_buffer entry is called to free a single buffer; free_chain is called to free a chain of buffers, and return the number of buffers in the freed chain. The free_space entry is called to free a block of arbitrary size. A block allocated by get_space must be freed by free_space; buffers allocated by get_buffer or get_chain must be freed by free_buffer or free_chain, although buffers that were allocated separately can be freed as a chain, and vice versa. The free_buffer and free_chain entries deduce the size of each buffer being freed from its size code; free_space must be told the size of the block to be freed. The last buffer in a chain to be freed by free_chain is identified by a forward pointer of 0. .spb 2 Each buffer or block freed is threaded into the free chain at the appropriate point (the free chain is sorted by increasing address). If the newly free block is immediately preceded and/or followed by an already free block, the adjacent blocks are combined into a single, larger free block. .spb 2 Once the supplied buffers have been freed, the freeing entries process any pending "space_needed" requests, as explained below. .ifi hit "K|space_needed" .ifi l2h "Needed Space" .ifi hit "K|wakeup" .ifi hit "K|space_needed" When tty_write is unable to allocate any buffers in which to build an output chain, it calls tty_space_man$needs_space to ensure that a wakeup is sent to the calling process when more buffers become available. This entry sets the "space_needed" flag in the channel's LCTE, and also sets a flag in the tty_buf header to indicate that there is an LCTE with its space_needed flag set. .spb 2 .ifi hit "K|logical channel table (LCT)" .ifi hit "K|wakeup" .ifi hit "K|space_needed" When tty_space_man finishes freeing a buffer, chain, or block, it checks the flag in the tty_buf header and, if it is on, searches the entire LCT for entries with the space_needed flag on. For each one it finds, it calls channel_manager$interrupt with an interrupt type of "space_available"; tty_interrupt handles this interrupt by sending a wakeup to the process using the channel. This enables each process to retry its call to tty_write; of course, there is no guarantee that it will now be possible to allocate buffers for the channel, but if not, tty_space_man$needs_space is called again. When tty_space_man is finished searching the LCT, it checks again to see if any space_needed flags have been turned on again as a result of any of the interrupts it generated. If not, it turns off the flag in the tty_buf header. .ifi l1h "Assembler Language Utilities" .ifi l2exact "tty_util_ Module" .ifi l2toc "tty_util_ Module" .ifi hit "K|tty_read" .ifi hit "K|tty_util_" .ifi hit "K|tty_write" The tty_util_ module contains entries called by tty_read and tty_write to scan and/or translate strings of input or output characters using EIS instructions. The occasions on which these entries are called are described in Section!5; a brief summary of the available entries is included here. .spb 2 .inl 12 .unl 7 mvt .ifi hit "K|translation table" .brf translates a string under control of a translation table. .spb .unl 7 scm .brf searches a string for a character with its 400(8) bit or its 200(8) bit on, depending on how it is called. .spb .unl 7 tct .ifi hit "K|indicator" .ifi hit "K|conversion~table" .brf searches a string for a character whose indicator in a conversion table is nonzero, and returns the position of the character and the value of the indicator. .spb .unl 7 find_char .brf like tct, except that it also checks explicitly for characters with either of their two high-order bits on, and for white space combinations beginning with a blank. .spb .unl 7 illegal_char .brf scans a string for a character with either or both of its two high-order bits on. .spb 2 .inl 0 All of these entries are provided with a pointer to, and the length of, the string to be processed. All except mvt update the pointer and the length to reflect how much of the string was scanned before the specified condition was met. .ifi l2exact "dn355_util Subroutine" .ifi l2toc "dn355_util Subroutine" .ifi hit "K|dn355_util" .ifi hit "K|peripheral control word (PCW)" .ifi hit "S|PCW~see peripheral control word" .ifi hit "K|direct interface adapter (DIA)" The dn355_util subroutine contains a single entry, dn355_util$compute_parity, which is used to ensure that the PCW sent to the DIA for interrupting, bootloading, or dumping the FNP has odd parity. If the parity of the PCW is not already odd, the subroutine makes it so by turning on bit!22. .fin .inl 0 .brp  s7.mcs.compin 10/18/84 1051.9rew 12/09/81 1149.0 44154 .ifi init_plm "AN85-01" .srv section 7 .ifi l0h "Initialization of Hardcore Data Bases" The dynamic data bases used by ring!0 Multics Communication System, i.e., dn355_mailbox, dn355_data, tty_area, tty_tables, and tty_buf, are initialized in several stages: preliminary initialization during hardcore initialization (collection 1); LCT initialization during answering service initialization (after a startup, multics, or go command); and the initialization of per-channel and per-multiplexer data bases as the multiplexers are initialized and loaded. These data bases are all described in Section!2. .ifi l1h "Preliminary Initialization" .ifi hit "K|configuration~cards" .ifi hit "K|circular buffer" .ifi hit "K|free~pool" The program called initializer, which runs during collection!1 initialization, calls fnp_init to process configuration cards relating to Multics Communication System. (See the M__u_l_t_i_c_s O__p_e_r_a_t_o_r_s'_ H__a_n_d_b_o_o_k, Order No.!AM81, for a description of these cards.) First it calls get_main to allocate wired storage for tty_buf, using the size specified on a PARM TTYB card; if no such card is present, a default size of 5120 words (5K) is used. The absolute address of the base of tty_buf is calculated and stored. The size of the circular buffer is determined from a PARM TTYQ card; if no such card is present, a default size of 256 words is used. The necessary amount of space for the circular buffer is reserved at the end of the tty_buf header; the remainder of tty_buf, starting at the next 0 mod 16 address, is marked as the free pool, which initially consists of one free block. Then the areas in tty_tables and tty_area are initialized as empty areas. .spb 2 .ifi hit "K|configuration~deck" Finally, dn355_data and dn355_mailbox are initialized in accordance with all FNP cards found in the configuration deck. These cards are checked for validity and consistency; for each one, the IOM number and IOM channel assigned to the specified FNP are copied into the entry in dn355_data for that FNP, and the mailbox pointer is set to address the correct mailbox area. Calls are made to iom_manager to assign dn355$interrupt as the interrupt handler for the specified IOM channels; in addition, fnp_init calls iom_manager$iom_set_list for each FNP, since iom_manager requires this call in order to perform I/O properly. .ifi l1h "LCT Initialization" .ifi hit "K|answering service" .ifi hit "K|channel definition table (CDT)" .ifi hit "K|LCTE" .ifi hit "K|logical channel table (LCT)" .ifi hit "K|logical channel name table (LCNT)" During answering service initialization, the initializer process scans the CDT to determine how many communications channels are defined; based on this, and on the value specified for the spare_channel_count keyword, it determines the number of LCTEs required, and calls the gate entry hphcs_$lct_init. This call is forwarded to priv_channel_manager$lct_init, which calls tty_space_man$get_space to allocate space for the LCT and also allocates space for the LCNT in tty_area. It then assigns LCTEs at the beginning of the LCT to however many FNPs are configured, and sets their special_lock flags to indicate that channel_manager is not to attempt to lock these channels. .ifi l1h "Multiplexer Initialization" .ifi hit "K|multiplexer~initialization" .ifi hit "K|multiplexer~module" .ifi hit "K|fnp_info" .ifi hit "K|line~adapter" .ifi hit "K|high-speed line adapter (HSLA)" .ifi hit "K|low-speed line adapter (LSLA)" .ifi hit "S|HSLA~see high-speed line adapter" .ifi hit "S|LSLA~see low-speed line adapter" Before loading a multiplexer, the answering service calls hphcs_$init_multiplexer, which call is forwarded through priv_channel_manager to the initialization entry of the appropriate multiplexer module, as explained in Section!3. In the case of an FNP, this entry is fnp_multiplexer$init_multiplexer. It initializes the specified FNP's fnp_info structure in dn355_data, and allocates a contiguous block of space for PCBs for all the subchannels of the FNP. The array of channel names passed to this entry is sorted in ascending order, so init_multiplexer knows that the channels for any given line adapter (HSLA or LSLA) are contiguous; this enables it to assign them contiguous PCBs and store the PCB index of the first channel on each adapter in the fnp_info structure, thereby reducing the number of entries dn355 has to scan in order to find the PCB for a channel specified in a submailbox from the FNP. .ifi l1h "Channel Initialization" .ifi hit "K|channel~initialization" Before issuing a "listen" control operation to a nonmultiplexed channel, the answering service calls hphcs_$init_channel; this call is forwarded through priv_channel_manager to tty_index$init_channel, which allocates the channel's WTCB and TCB as explained in Section!5. .fin .inl 0 .brp  s8.mcs.compin 10/18/84 1051.9rew 12/09/81 1149.0 148194 .ifi init_plm "AN85-01" .srv section 8 .ifi l0h "Tools and Debugging Aids" .ifi hit "K|error messages" .ifi hit "K|system crashes" A few commands are provided to allow a user process to obtain information about the current state of the CS portion of Multics Communication System and its associated databases; they can be helpful in debugging Multics Communication System. This section describes these commands. It also includes information on error messages that Multics Communication System may write on the syserr console and/or into the syserr log, as well as some pointers on analyzing Multics Communication System-related system crashes. .ifi l1h "Tools" .ifi hit "K|tty_meters command" .ifi hit "K|tty_dump command" .ifi hit "K|tty_analyze command" .ifi hit "K|metering" Three commands are discussed in this section: tty_meters, which displays the metering information kept in tty_buf by Multics Communication System (see Section!2 for descriptions of the individual structure items); tty_dump, which displays the current state of a single channel; and tty_analyze, which displays information extracted from a system dump. Command descriptions of some of these commands appear in Appendix!B; others appear in the M__u_l_t_i_c_s_A__d_m_i_n_i_s_t_r_a_t_o_r_s'__M__a_n_u_a_l_(_M_A_M_)_ -- C__o_m_m_u_n_i_c_a_t_i_o_n_s, Order!No.!CC75. .ifi l2exact "tty_meters" .ifi l2toc "tty_meters" The tty_meters command uses information stored in the header of tty_buf to derive statistics on the behavior of Multics Communication System and of the various communications channels. It uses two temporary segments to keep two copies of tty_buf, and one for a copy of dn355_data. The two copies of tty_buf can be thought of as the "current" copy and the "old" copy. The current copy is filled in from ring 0 every time the command is invoked; the old copy, initially all 0, is updated from the current copy whenever the -reset control argument is used. The statistics printed by the command reflect the differences between the values in the old copy and in the current copy. The command description for tty_meters is in M_A_M_!--!C__o_m_m_u_n_i_c_a_t_i_o_n_s, Order!No.!CC75. .brp The command can operate in either normal mode or long mode. (It operates in normal mode unless the -long control argument is used.) In normal mode, only the header of tty_buf is copied from ring 0, up to but not including the first word of the circular buffer. In long mode, all of tty_buf is copied, and additional information on how many terminals of each type are dialed up is derived by scanning all the WTCBs. The values of wtcb.send_output, wtcb.rflag, and wtcb.wflag are also used in long mode to determine how many channels are currently sending input or receiving output. Because the length of the header is specified in ring_zero_meter_limits_ASCII_, use of long mode requires access to the phcs_ gate. .ifi hit "K|circular buffer" .spb 2 The copy of dn355_data is used to extract the version identifier of the Multics Communication System running in each configured FNP. This information is displayed at the beginning of the output of tty_meter, along with the FNP core image name specified in the CDT. The average number of free buffers in each FNP is also derived from dn355_data. .ifi l2exact "tty_dump" .ifi l2toc "tty_dump" .ifi hit "K|tty_dump command" .ifi hit "K|logical channel name table (LCNT)" .ifi hit "K|answer table" The tty_dump command displays various databases of a specified communications channel. It makes copies of tty_area, tty_buf, and dn355_data. It can be used to display information derived from either a running system or an FDUMP. Use of this command with a live system requires access to the phcs_ gate. The channel can be selected by specifying either the channel name or a person name. If a channel name is specified, the LCNT is searched for that channel's entry. If a person name is specified, the answer table is searched for a logged-in user having that name; if one is found, information is displayed about the communications channel(s) assigned to that user's process by the answering service. The command description for tty_dump is in M_A_M_!--!C__o_m_m_u_n_i_c_a_t_i_o_n_s, Order!No. CC75. .spb 2 .ifi hit "K|multiplexer~type" .ifi hit "K|parent multiplexer" .ifi hit "K|physical channel" .ifi hit "K|LCTE" The format of the information displayed by tty_dump depends on the multiplexer type of the specified channel. For a nonmultiplexed channel, the WTCB and TCB are displayed, along with the contents of any current input and/or output chains. The -subchannel control argument may be used to indicate that the database of the parent multiplexer, or at least that portion of it that concerns the specified channel, is to be displayed (for a physical channel of an FNP, this means to display the channel's PCB). The -all control argument specifies that the databases associated with the channel at all levels of multiplexing (up to and including the physical FNP channel) are to be displayed. The -lcte control argument indicates the LCTE of the specified channel (and its parents if -all is specified) is to be displayed. .spb 2 For channel types other than nonmultiplexed ("tty") channels, a subroutine that displays the associated multiplexer databases must be provided. Its name must be of the form TYPE_dump_, where TYPE is the name of the multiplexer type (e.g., user1_dump_ for the multiplexer type "user1"). The calling sequence of this subroutine is described below, using a multiplexer type of "user1" for the sake of example. .ifi hit "K|multiplexer~type" .spf .all .inl 10 .unl 5 declare user1_dump_ entry (pointer, pointer, pointer, fixed bin, bit (1)); .spf .unl 5 call user1_dump_ (ttybp, areap, database_ptr, subchan, brief_sw); .alb .spb .inl 0 where: .spf .inl 12 .unl 7 ttybp (Input) .brf is a pointer to the base of tty_buf. .spb .unl 7 areap (Input) .brf is a pointer to the base of tty_area. .spf .unl 7 database_ptr (Input) .brf is a pointer to the multiplexer's database. .spb .unl 7 subchan (Input) .brf is the subchannel number of the channel about which information was requested. A subchannel number of -1 indicates that information is requested for all subchannels. .spb .unl 7 brief_sw (Input) .brf is "1"b if the -brief control argument to tty_dump was specified; otherwise it is "0"b. .spb 2 .inl 0 A system-supplied example of such a subroutine is vip7760_dump_. .spb 2 .inl 0 An entry to tty_dump called print_chain is provided to allow multiplexer dumping subroutines to share the code in tty_dump that displays the contents of an input or output chain. Its calling sequence is described below. .ifi hit "K|input chain" .ifi hit "K|output chain" .spf .all .inl 10 .unl 5 declare tty_dump$print_chain entry (pointer, char(*), fixed bin, bit (1)); .spf .unl 5 call tty_dump$print_chain (ttybp, chain_name, chain_start, brief_sw); .alb .spb .inl 0 where: .spb .inl 12 .unl 7 ttybp (Input) .brf is a pointer to the base of tty_buf. .spf .unl 7 chain_name (Input) .brf is a character string identifying the type of chain; it is printed before the contents of the chain. .spb .unl 7 chain_start (Input) .brf is the offset in tty_buf of the first buffer in the chain. .spb .unl 7 brief_sw (Input) .brf is "1"b if only the offset, size, and flags associated with each buffer are to be displayed. If it is "0"b, the contents of each buffer are displayed as well. .inl 0 .brf .ifi l2exact "tty_analyze" .ifi l2toc "tty_analyze" .ifi hit "K|tty_analyze command" .ifi hit "K|segment dump" .ifi hit "K|multiplexer~database" .ifi hit "K|logical channel name table (LCNT)" .ifi hit "K|physical channel" .ifi hit "K|delay~queue" The tty_analyze command produces formatted output describing the contents of tty_buf taken from a Multics segment dump. The command description for tty_analyze appears in Appendix B. It calls the extract command to copy tty_buf, tty_area, dn355_mailbox, and dn355_data from a specified dump in >dumps. It then loops through the array of PCBs for each configured FNP; for each one, it displays the contents of the PCB. If the channel is not multiplexed, it then displays the contents of the WTCB; otherwise it calls a subroutine (described below) to display the multiplexer database, and searches the LCNT for all subchannels of the multiplexed channel. This process is repeated through all levels of multiplexing (down to the WTCB level) until the databases of all descendants of each physical channel have been displayed. For each channel, the delay queue, if any, pointed to by the LCTE is also displayed. In addition, for each channel having a read and/or write chain it prints the addresses of all buffers in each such chain. If the -long control argument is specified, the contents of each buffer are also printed (in octal). Each buffer (and each database) thus accounted for is marked with a special pattern so that multiple uses of a single block can be detected. .spb 2 .ifi hit "K|multiplexer~type" For each multiplexer type (other than "mcs" and "tty"), a subroutine must be supplied that displays the contents of multiplexer databases. Its name must be of the form TYPE_analyze_, where TYPE is the name of the multiplexer type (e.g., user1_analyze_ for the multiplexer type "user1"). The calling sequence of this subroutine is described below, using a multiplexer type of "user1" for the sake of example. .spf .all .inl 10 .unl 5 declare user1_analyze_ entry (pointer, pointer, fixed bin, entry, bit(1)); .spb .unl 5 call user1_analyze_ (ttybp, areap, devx, check_used_entry, longsw); .alb .brp .inl 0 where: .spb .inl 12 .unl 7 ttybp (Input) .brf is a pointer to the base of tty_buf. .spb .unl 7 areap (Input) .brf is a pointer to the base of tty_area. .spb .unl 7 devx (Input) .brf is the device index of the multiplexer channel. .spb .unl 7 check_used_entry (Input) .brf is the entry to be called to mark a buffer or control block as having been accounted for; the calling sequence is described below. .spb .unl 7 longsw (Input) .brf is "1"b if the "-long" control argument to tty_analyze was specified; otherwise it is "0"b. .spb 2 .inl 0 A system-supplied example of such a subroutine is vip7760_analyze_. .inl 0 .spb 2 .ifi hit "K|tty_analyze command" For every control block or buffer that it displays, the subroutine should call the check_used_entry passed to it by tty_analyze to account for the space. The calling sequence of this entry is as follows: .spf .inl 10 .all .unl 5 declare check_used_entry entry (pointer, fixed bin) variable; .spf .unl 5 call check_used_entry (block_ptr, nwords); .alb .inl 0 .spb 2 where: .spb .inl 12 .unl 7 block_ptr (Input) .brf is a pointer to the buffer or block to be accounted for. .spb .unl 7 nwords (Input) .brf is the length of the buffer or block in words. .inl 0 .spb 2 .ifi hit "K|tty_analyze command" .ifi hit "K|delay~queue" .ifi hit "K|pseudo-DCW" After all channels have been checked, tty_analyze displays the addresses of all FNP delay queue entries created by dn355 (see Section 4). Then it searches dn355_mailbox for any currently-active submailboxes that contain an I/O command of WTX, and displays the addresses of any pseudo-DCW lists pointed to by such mailboxes. If the -long control argument is specified, the contents of these blocks are also displayed. Then tty_analyze follows the free block chain starting at tty_buf.free, and any invalid chain pointers are reported. Finally, a list is printed of the addresses of all blocks in tty_buf not otherwise accounted for. .ifi l1h "Syserr Messages" .ifi hit "K|error messages" The messages discussed here are those messages generated by calls to syserr without crashing Multics. (Crashes generated by Multics Communication System are discussed later in this section.) Almost all nonfatal error messages produced by Multics Communication System are the result of conditions detected by the FNP. .ifi l2h "FNP Crashes" .ifi hit "K|FNP~crashes" .ifi hit "K|mailbox~header" When an FNP crashes (as described in Sections!13 and 16) it sends an "emergency" interrupt to the CS; dn355 handles this interrupt by interpreting the crash data in words!6!and!7 of the mailbox header of that FNP (mailbox headers are described in Sections!2 and 16). Using the type of FNP fault and the contents of the FNP instruction counter, it looks up the appropriate message in dn355_messages, and prints it on the syserr console. It then signals the process that bootloaded the FNP (which is generally the initializer process), and reports hangup conditions on all channels connected to that FNP. .ifi l2h "Other FNP Messages" When a submailbox from the FNP contains an "error message" operation code, the error code included in the command data is used to index an array of messages in dn355_messages to find the one to print on the console. These messages describe nonfatal FNP errors, such as certain types of DIA I/O error, possibly runaway HSLA subchannels, and the like. They do not normally require any action, but they are sometimes useful in tracking down other problems. If some FNP channels do not seem to be behaving properly, or if an FNP crash is not readily analyzable, any error messages sent by that FNP may provide useful additional information. These messages all indicate probable hardware problems. .brf .ifi l1h "Crashes Generated by Multics Communication System" .ifi hit "K|crash" .ifi hit "K|tty_space_man" .ifi hit "K|tty_analyze command" The CS portion of Multics Communication System elects to crash (by calling syserr) if certain inconsistent or "impossible" conditions are detected; for example, an attempt to unlock a lock that turns out to have been locked by some other process, an unrecognizable submailbox sent from the FNP, or an interrupt on an unrecognized level. These conditions are all quite rare, and the associated syserr messages are generally self-explanatory. It is often a good idea to dump the FNP as well as the CS when an Multics Communication System-related crash occurs, particularly one resulting from a faulty submailbox. There is a class of errors detected by tty_space_man (see Section!6) indicating that the buffer pool is in an inconsistent state; tty_analyze may be useful here, as is visual examination of a dump of tty_buf. In general, dn355_mailbox, dn355_data, and tty_buf should all be dumped after an Multics Communication System-related or Multics Communication System-suspected crash. .spb 2 Two particular types of crash message are insufficiently explicit to be self-explanatory; both are accompanied by codes that can be used to determine the nature of the error. These codes are discussed below. .ifi l2h "Lock Errors" .ifi hit "K|lock errors" A message of the form: .spb .fif dn355: lock ^= processid, error = @_n .spb 2 .fin indicates that dn355 attempted to unlock a lock but discovered that the lock did not contain the ID of the currently running process. The code @_n can have either one of the following values: .spb .inl 11 .unl 5 5 -- the circular buffer lock was invalid .spb .unl 5 6 -- dn355$interrupt found the FNP channel lock invalid .inl 0 .brf .ifi l2h "Free Space Errors" .ifi hit "K|free~space errors" A message of the form: .spb tty_space_man: error of the Nth kind: ERR_TYPE .spb indicates an inconsistency or other problem in the buffer pool. These conditions are detected by tty_space_man (see Section!6.) .spf N and ERR_TYPE can have the following values: .spf .inl 30 .unl 25 N_ E_R_R_-_T_Y_P_E_ E__x_p_l_a_n_a_t_i_o_n .spf .inl 30 .unl 25 1 Mylock error A process attempting to lock the tty_buf lock already had it locked .spf .unl 25 2 Unlock error A process attempting to unlock the tty_buf lock did not have it locked .spf .unl 25 3 Bad address One of the freeing entries was called with an address outside the free pool .spf .unl 25 4 Already free An attempt was made to free space that was already free .inl 0 .fin .brp  s9.mcs.compin 10/18/84 1051.9rew 12/09/81 1149.0 69678 .ifi init_plm "AN85-01" .srv section 9 .ifi l0h "Overview of the FNP Software" .ifi l1h "Responsibilities of the FNP" The FNP-resident portion of Multics Communication System has the following major functions: .spb .inl 10 .unl 5 1. controlling the various communications channels in accordance with the appropriate communications protocols; .spb .unl 5 2. keeping the CS informed of the current state of each channel; .spb .unl 5 3. presenting input data from the various channels to the CS; .spb .unl 5 4. transmitting output supplied by the CS to the appropriate channels; .spb .unl 5 5. performing all echoing functions supported by Multics Communication System (linefeed and carriage return echo, tabs echoed as spaces, echoplex, replay). .inl 0 .ifi l1h "Structure of FNP Multics Communication System" The major components of the FNP software are listed below: .spb .inl 10 .unl 5 1. Scheduler - handles interrupts, decides which routine to run, manages timers. The scheduler is discussed in greater detail in Section!@11. .spb .unl 5 2. Terminal control functions: control tables and the control table interpreter - implement individual communications protocols, keep track of the state of each channel. These functions are discussed in greater detail in Section!12. .spb .unl 5 .brn 8 3. Hardware managers - control the direct interface adapter (DIA), low-speed line adapter (LSLA), high-speed line adapter (HSLA), and the FNP console. Hardware managers are discussed in Section!13. .spb .unl 5 4. Utilities - buffer space management, fault handling, tracing functions, etc. Utilities are discussed in Section!14. .spb 2 .inl 0 Each of these components is implemented by one or more modules written in the FNP assembler language, 355MAP. Each module is assembled separately using the map355 command, which invokes the assembler (in an ordinary Multics process) under GCOS simulation; the assembled modules are combined into a "core image" for the FNP by the bind_fnp command. The resulting core image is loaded into the FNP by the Multics initializer at system initialization time, or at any time in response to the operator command load_mpx. See M_A_M_-C__o_m_m_u_n_i_c_a_t_i_o_n_s, Order No.!CC75 for descriptions of the map355 and bind_fnp commands, and Section!15 for a description of the bootloading of the FNP. .ifi hit "K|core image" .spb 2 The modules that compose the core image are listed below. For each module, the long or "external" form of the name is the one used in specifying the source and object segments for the module, and is the name used in this document to refer to the module; the short or "internal" form is the symbol that defines the module within the core image and is stored in the module itself as part of the "module chain" used in printing dumps of the FNP (see Section!16). .spb 2 .inl 5 .inl 45 .unl 40 E__x_t_e_r_n_a_l__n_a_m_e I__n_t_e_r_n_a_l__n_a_m_e C__o_m_m_e_n_t_s .spb .unl 40 scheduler sked .spb .unl 40 interpreter intp control table interpreter .spb .unl 40 control_tables ctrl main control tables module .spb .unl 40 other control ---- see Section!12 .brf .unl 40 tables modules .spb .unl 40 dia_man dia DIA manager .spb .unl 40 lsla_man lsla LSLA manager .spb .unl 40 hsla_man hsla HSLA manager .spb .unl 40 console_man cons FNP console manager .spb .unl 40 utilities util .spb .unl 40 ic_sampler icsamp meters values of the instruction counter at interrupt time (see Section 11) .spb .unl 40 trace trac performs memory tracing functions (see Sections!14 and 16) .spb .unl 40 breakpoint_man bkpt manages breakpoints in control tables (see the description of the debug_fnp command in Appendix B) .spb .unl 40 init init FNP initialization program .fin .inl 0 .ifi l2h "Data Bases" .ifi hit "K|terminal information block (TIB)" .ifi hit "S|TIB~see terminal information block" A variety of common databases are used by all these modules to communicate information about the states of the various channels and hardware adapters; the data bases are described in Section!10. The most important of these is the terminal information block (TIB); there is one TIB for every configured channel, which describes the current state of the channel. The TIB is referenced by almost every module in the FNP; as a matter of software convention, the address of the TIB for the channel of current interest is held in index register 1. .ifi l1h "Channel Management" .ifi hit "K|control table" It may be convenient to view each channel as being managed by its own "process" in the FNP, where the state of the "process" is described by its TIB. The control tables are run in such a "process" whenever there is any work to do for the associated channel; when the "process" is not running, a field in the TIB (t.cur) identifies the place in the control tables at which execution last stopped. .ifi l1h "Interrupts and Scheduling" .ifi hit "K|interrupt" The FNP is driven by interrupts, which come primarily from four sources: .spb .inl 10 .unl 5 1. the DIA, either sent from the CS or used to signal the completion of DIA I/O; .spb .unl 5 2. the LSLA reporting status resulting from the processing of an input or output frame; .spb .unl 5 3. the HSLA reporting status for a particular subchannel; .spb .unl 5 4. the interval timer, when a software-established timer runs out. .spb 2 .inl 0 A routine that handles an interrupt does very little other than identifying the reason for the interrupt and scheduling another routine to do the work required by the event that caused the interrupt; for example, the control tables are never run at interrupt time, but may be either scheduled to run as a result of a timer interrupt or called by another scheduled routine. The interrupt and scheduling mechanisms are described in Section!@11. .ifi l1h "Data Paths" .ifi l2h "Input" .ifi hit "K|break character" .ifi hit "K|control table" .ifi hit "K|submailbox" When a user types a message-ending character (typically newline) an interrupt is generated (on an HSLA channel) or the character is recognized in an LSLA input frame, and "break character" status is sent to the control tables for the channel; the address of the beginning of the input message is stored in the TIB. The control tables, possibly after examining the data, execute a "sendin" operation block, which queues up a request for DIA I/O. When dia_man runs, it fills in a submailbox either with the input itself or with an operation code saying that input is available and a tally of characters in the message. In the latter case, the CS replies with another submailbox telling the FNP where to put the data; dia_man builds a DCW list to copy the data from FNP buffers into CS main memory, and when the I/O completes it frees the buffers in the FNP. .ifi l2h "Output" .ifi hit "K|pseudo-DCW" .ifi hit "K|control table" .ifi hit "K|interpreter" When output is ready to be sent from the CS, the CS prepares a mailbox telling the FNP the location of a block of "pseudo-DCWs" containing the addresses and tallies of output buffers in the CS. After reading this submailbox, dia_man reads in the pseudo-DCWs, and uses the information in them to read the data from CS memory into buffers in the FNP. The address of the first of these buffers is stored in the TIB, and dia_man calls the interpreter to start the control tables. The latter generate a call to lsla_man or hsla_man to send the data characters to the appropriate channel; when the last character in a buffer is sent to the channel, the buffer is freed. .fin .inl 0 .brp  s11.mcsa.compin 10/18/84 1051.9rew 12/14/81 1533.2 116568 .ifi init_plm "AN85-01" .srv section 11 .srv add_date "12/81" .srv add_letter "A" .ifi l0h "Scheduler" The FNP scheduler is responsible for controlling the execution environment of the FNP. The scheduler consists of three main parts: the master dispatcher, the secondary dispatcher, and the timer management routines. .ifi l1h "Master Dispatcher" .ifi hit "K|master dispatcher" .ifi hit "K|interrupt~cells" .ifi hit "K|interrupt~vector" .ifi hit "K|jump table" The master dispatcher is normally run in response to an interrupt. Interrupts in the FNP are handled by setting bits in interrupt cells at location 400-417(8). These cells are maintained by the hardware and scanned in a priority order. Whenever one of these bits is found to be on, the interrupt is presented to the processor, which computes an address based on .cbn A which cell is set. This address is in the range 0-377(8), .cbf which represents the 256 possible interrupt cells in the FNP. If the processor is not inhibited, it executes a "tsy" indirect to the computed address. This location contains another level of indirect addressing, to the interrupt processing routine. However, if all interrupts of a certain class (e.g., HSLA or LSLA interrupts) go to a common routine, no information as to which device caused the interrupt is available. To solve this problem, the location in the interrupt vector contains not the address of the interrupt processing routine, but instead the address of a three-word jump table. This table has the following format: .spb .bba -| |* zero -" *" tsy interrupt_hndlr -" *" coded word ~- ~* .bea .spb 2 .inl .ifi hit "K|interrupt~vector" .ifi hit "K|jump table" There is a separate table for each interrupt vector address (0-400(8)), and these tables are set up by the hardware managers that run the device. Thus, when an interrupt occurs, the hardware will tsy indirect through the interrupt vector to the 3-word jump table, store the instruction counter (IC) at the time of the interrupt in the first word, and execute the tsy in the second word. This stores the address of the third word of the jump table in the first word of the interrupt handler, so the software can determine where the interrupt occurred and which device caused it. .spb 2 In general, the tsy instruction in the second word of the jump table is a tsy to invp (the interrupt vector processor), an entry in the scheduler. This enables the scheduler to be invoked on each interrupt, and allows interrupt priorities to be assigned and enforced by the software. .spb 2 .srv add_date "" .srv add_letter "" The third word of the jump table, abbreviated 3wjt, has the following form: .spb .bba -|0 3 4 10 11 17*| |" |" ioc devid mod -~ ~" ~" *~ 4 7 7 .spb .inl 0 .bea where: .spb .inl 12 .unl 12 1. ioc .brf is the IOM channel number of this device. .spb .unl 12 2. devid .brf is a device specific identifier for multiple device channels (e.g., HSLA number and subchannel). .spb .unl 12 3. mod .brf is a scheduler module number for the module to be called for this interrupt. .spb 2 .inl 0 When invp is entered, it saves the third word of the jump table, the IC at the time of the interrupt, and all machine registers. It then enters the master dispatcher, which searches its tables looking for the highest priority interrupt. If the new interrupt is highest priority, it is run first; otherwise, the previous interrupt routine is restarted. Note that this scheme allows the actual interrupt service routine (e.g., hintr for HSLAs) to run uninhibited, to allow higher priority interrupts to occur. An interrupted occurrence of an interrupt handler is always allowed to complete before a new instance of the same interrupt handler is run. .spb 2 .brn 9 .ifi hit "K|master dispatcher" .ifi hit "K|secondary dispatcher" When the interrupt handler is finished, it returns to the master dispatcher (via a tra mdisp) and the master dispatcher clears this entry from its tables and starts a search for a new routine to run. The last entry in the dispatcher's table is an entry for the secondary dispatcher. If any routines have been queued to be run, this entry is marked active. Thus, if no interrupt routines are to be run, the secondary dispatcher runs a queued routine. .spb 2 This scheme causes the secondary dispatcher to be considered as if it were the lowest possible priority interrupt routine. If the secondary dispatcher, or a routine it is running, is interrupted, the master dispatcher saves all of the information as before, but when it is about to restart the queued routine, control is not returned to the point of interruption but instead to a secondary entry point in the secondary dispatcher. The secondary dispatcher can then reexamine its queues and run the highest priority routine and complete the interrupted routine at a later time. .ifi l1h "Secondary Dispatcher" .ifi hit "K|secondary dispatcher" Most interrupt routines called by the master dispatcher collect information about the interrupt and queue a routine to process it. Queueing of a routine is done via the scheduler entry dspqur. This entry gets its arguments in three registers as follows: .spb .bba -|0 11 12 17*| |" a reg time_delay priority -" ~" *" q reg routine_to_run -" *" x1 reg info_ptr -~ *~ .bea .spb 2 .inl 0 where: .spb .inl 12 .unl 12 1. time_delay .brf is the delay, in seconds, before the routine is to be run. .spb .unl 12 2. priority .brf is a scheduler priority (explained below). .spb .unl 12 .brn 7 3. routine_to_run .brf is the address of the routine to be executed when its turn comes. .spb .unl 12 4. info_ptr .brf is passed to the routine in index register 1 at the time it is run. .inl 0 .spb 2 The time_delay may be zero, indicating that the routine is to be run as soon as possible. The priority is one of three groups: high (0-7), medium (10-17), or low (20-27). It is very important to note that the scheduler does not preempt a routine in a group with another routine in the same group, i.e., a routine started in the high group always runs to completion before any other routine is run. This implies that if a routine is scheduled in the medium priority group and an interrupt occurs that schedules a high priority routine, the high priority routine is run immediately. Thus, a medium priority routine cannot use any of the same storage or subroutines (unless the subroutine is inhibited) as a high priority routine. The current implementation of Multics Communication System uses only the high priority group, to avoid problems caused by two routines of different priority groups calling the same subroutine. .ifi l1h "Timer Management" .ifi hit "K|timer management" Two uses are made of the timer management mechanism in the scheduler: the delayed queueing function, and terminal control timing functions. .spb 2 .brn 5 The delayed queueing function is invoked via a call to dspqur, the dispatcher queueing routine, as described above. When dspqur notices that a delay has been specified, a queue entry is made in a special time delay dispatch queue, and the clock is updated if required. .spb 2 Terminal control timing functions are handled from the control_tables by the interpreter via the scheduler entry setime. Two arguments are passed to setime as follows: .spb .bba -| *| a reg time -" *" x1 reg tib_ptr -~ *~ .bea .spb 2 .inl 0 .brn 7 where: .spb .inl 12 .unl 12 1. time .brf is the number of seconds to wait before a timeout occurs, and can be zero to reset the current timer for this channel. .spb .unl 12 2. tib_ptr .ifi hit "K|terminal information block (TIB)" .brf is the address of the terminal information block (TIB) for the channel. .spb 2 .inl 0 .brn 4 When setime is called, it searches its current queue for an entry for the requested TIB, and frees it from the list if one is found. It then makes a new entry in its special TIB queue for this TIB. The clock is updated if necessary. .spb 2 .ifi hit "K|FNP~clock" .ifi hit "K|interval timer" The FNP clock is a 36-bit simulated real-time clock that counts the number of milliseconds since the bootload of the FNP. This clock is maintained by using the interval timer of the FNP to count real milliseconds. The simulated real-time clock always contains the time at which the interval timer will run out; i.e., when the interval timer runs out, the simulated real-time clock contains the current time. If no timers are required by either the TIB timer or the delay queue, then the inverval timer is set to its maximum value (2**18 milliseconds, approximately 4.4 minutes) and this amount is added to the simulated real-time clock. .spb 2 .brn 30 When either of the timer routines is called, it gets the current time by subtracting the interval timer from the simulated real-time clock. The routine then adds the number of milliseconds of delay required by its caller and saves this information as the real time at which timeout is to occur. The delay queue manager keeps 6-word queue entries for each delayed routine as follows: .spb 1 .bba -| *| forward pointer -" *" routine addr -" *" real time clock value (two words) -" *" register x1 -" *" priority -~ *~ .bea .spb 2 .inl 0 These entries are threaded with the earliest time first. The TIB timeout manager, setime, keeps 2-word queue entries, a forward pointer and the TIB address, with the clock value stored at t.time. The TIB timeout manager also threads the entries with the earliest time first. .spb 2 Updating the clock consists of inspecting the first entry on the list just rethreaded and comparing the time in that entry to the simulated real-time clock. If it is earlier, the simulated real-time clock and the interval timer are updated to the earlier value. .spb 2 When the interval timer runs out, both lists are searched for eligible timeouts. For each entry selected dspqur is called (with a zero delay value) and the entry is freed. Then the first entries of the two lists are compared to find the earliest time value and the simulated real-time clock and interval timer are updated with the new value. If no entries are present in either list, the time is set to the maximum. .ifi l1h "Elapsed Time Metering" .ifi hit "K|elapsed timer" The elapsed timer is used to meter the relative amount of time the FNP is idle, and optionally to record the parts of the system in which the most execution time is spent. The elapsed timer is set by software to run out and thus generate an interrupt every 50 milliseconds, at which time the elapsed time runout handler is executed. The 50-millisecond interval can be changed by use of the sample_time request to the debug_fnp command. .ifi l2h "Idle Time Metering" .ifi hit "K|idle time metering" .ifi hit "K|master dispatcher" The elapsed time runout handler examines the instruction counter at the time of the interrupt to see if it contains the address of the "dis" instruction in the master dispatcher that is executed if there is nothing to do. If the dis instruction address is present, an "idle" counter is incremented by one; otherwise, a "busy" counter is incremented. The idle_time request to the debug_fnp command (see Appendix!B) determines the amount of idle time by comparing these two counters. .ifi l2h "Instruction Counter Sampling" .ifi hit "K|instruction counter sampling" If the ic_sampler module is included in the core image, the elapsed timer runout handler calls an entry in it, icmon. If instruction counter sampling has been enabled by the debug_fnp request line "ic_sample start", icmon increments a "bucket" associated with the 16-word range of addresses in which the instruction counter at the time of the interrupt falls. This information can be examined by means of the ic_sample request to the debug_fnp command, as explained in the description of the debug_fnp command in Appendix!B. .fin .inl .brp  s12.mcsa.compin 10/18/84 1051.9rew 12/14/81 1543.5 904311 .ifi init_plm "AN85-01" .srv section "12" .ifi l0h "Terminal And Line Control" .pdl 68 Terminal and line control in Multics Communication System is managed by a set of control tables that consists of macros defining operations to be performed for each configured communications channel. The macros do not generate executable FNP instructions; rather, each macro generates a block of data (referred to as an operation block or "op block") which is recognized by a program called the interpreter. Loosely, a set of op blocks is executed on behalf of some channel; what this really means is that the interpreter is invoked to perform the operations specified by those op blocks. .ifi l1h "Organization of the Control Tables" .ifi l2h "Division Into Modules" .ifi hit "K|control table" .ifi hit "K|line~type" .ifi hit "K|core image" .ifi hit "S|ACU~see automatic call unit" The Multics Communication System control tables consist of several modules, and additional modules may be added as necessary. The main module, control_tables, contains the op block macros for the most commonly used line types, as well as tables defining various attributes of each line type. The other modules contain op blocks for various special-purpose line types or special types of line-control operations; these modules are optional and may be omitted from the Multics Communication System core image (as described in Section 17) if the relevant line type or special function is not used. The following supplementary control_tables modules are currently implemented: .spb .inl 10 .unl 5 acu_tables .ifi hit "K|automatic call unit (ACU)" .brf performs "dial out" operations to an automatic call unit (ACU) .spb .unl 5 autobaud_tables .ifi hit "K|automatic baud_rate detection" .brf performs automatic baud-rate detection on asynchronous HSLA subchannels .spb .unl 5 ards_tables .brf controls an ARDS-like terminal on a Bell 202C modem .spb .unl 5 t202_tables .brf controls a TermiNet 1200 terminal on a Bell 202C6 modem .spb .unl 5 g115_tables .brf implements the G115 synchronous line protocol (RCI) .spb .unl 5 vip_tables .brf controls a VIP 7705 display terminal in nonpolled operation .spb .unl 5 bsc_tables .ifi hit "K|binary synchronous" .brf controls a bisync binary synchronous subchannel of an HSLA .spb .unl 5 polled_vip_tables .brf controls a VIP 7700 or VIP 7760 subsystem in polled operation .spb .unl 5 ibm3270_tables .brf controls an IBM Model 3270 controller and associated displays and printers (bsc_tables must also be included) .inl 0 .ifi l2exact "T__a_b_l_e_s_I__n_c_l_u_d_e_d__i_n__t_h_e__c_o_n_t_r_o_l__t_a_b_l_e_s_M__o_d_u_l_e" .ifi l2toc "Tables Included in the control_tables Module" .ifi l3h "Header" At the base of control_tables is an array of pointers to tables that other parts of Multics Communication System need to be able to find. This array consists of the following: .spb .inl 10 .unl 5 word 0 .brf address of the first op block to be executed for all channels .spb .unl 5 word 1 .ifi hit "K|device~info tables" .brf address of an array of pointers to device info tables. This array is indexed by line type to find the device info table itself (described below) .spb .unl 5 word 2 .brf unused .spb .unl 5 word 3 .brf address of the device-type/speed table (described below) .spb .unl 5 word 4 .ifi hit "K|answerback" .brf address of the first op block to be executed when reading a terminal's answerback .inl 0 .ifi l3h "Device Info Table" There is a device info table for each line type. The device info table has the following format: .spb .bbk .bba -|0 8 9 17*| "| seq1 seq2 "- "~ "* unused "- "* keyboard address .ifi hit "K|keyboard addressing" "- "* printer address .ifi hit "K|printer addressing" "- "* flags "- "* "| nl cr "- "* tab bs "- "* upshift downshift "- "* break list break1 indicator "- "* break2 etc. -~ "~ *~ .bea .bek .spb 2 .inl 0 where: .spb .inl 12 .unl 7 seq1, seq2 .brf are the two sequence characters to be used in alternate messages to a synchronous channel; they are ignored for an asynchronous channel. .spb .unl 7 keyboard address .brf is the address of the default keyboard addressing string for this line type. .spb .unl 7 printer address .brf is the address of the default printer addressing string for this line type. .spb .unl 7 flags .ifi hit "K|terminal information block (TIB)" .brf indicate the default settings of certain flags in the terminal information block (TIB) and the software communications region: .spb .inl +5 .unl 5 bit 17 .brf is the default setting of tfctrl (keyboard and printer addressing required) .spb .unl 5 bit 16 .brf is the default setting of sffsct (default CCT is short--ignored for LSLA channels) .spb .unl 5 bit 15 .brf is the default setting of tfsftr (terminal uses case-shift characters) .spb 2 .inl 0 The following six characters are used in determining column position as a result of input or output. A character of all ones means that no applicable character exists for the specified line type. .spb .inl 10 .unl 5 nl is the newline character .spb .unl 5 cr is the carriage return character .spb .unl 5 tab is the horizontal tab character .spb .unl 5 bs is the backspace character .spb .unl 5 upshift is the uppercase shift character .spb .unl 5 downshift is the lowercase shift character .spb 2 .inl 0 .ifi hit "K|break list" The remainder of the device info table is the break list, used for identifying break (end-of-message) characters on an LSLA channel; it is ignored for HSLA channels. The indicator is either 775 (octal), which indicates that break character status should be signalled in response to the input character immediately following the character specified in break1, or it is the number of characters (1 to 7) in the break list; break character status is signalled when one of these characters is input. .brn 40 .ifi l3h "Device-Type/Speed Table" .ifi hit "K|line~type" .ifi hit "K|baud rate" This table is used at initialization time to derive the default line type of each channel from the supplied baud rate code. Each entry is one 18-bit word in the following format: .spb 2 .bbk .bba -|0 1 4 5 8 9 17*| -~A|~ MBZ |~ BC |~ TYPE *~ .bea .bek .spb 2 .inl 0 where: .spb .inl 12 .unl 12 1. A .brf is 0 for an asynchronous channel or 1 for a synchronous channel. .spb .unl 12 2. BC .brf is the baud rate code, encoded as in an HSLA table entry (see Section 10). .spb .unl 12 3. TYPE .brf is the default line type associated with a channel having the specified synchronous attribute and baud rate. .inl 0 .ifi l3h "Addressing Strings" .ifi hit "K|addressing strings" The addressing strings pointed to by the device info table (see above) are strings of up to four characters used to "address" (i.e., enable) the keyboards and printers of certain terminals. Normally, such strings are required only for IBM 1050- and 2741-like terminals; the strings provided for the "ASCII" line type are generally only used for Teletype Model 37 terminals. The use of these strings is determined by the setting of the TIB flag tfctrl, which can be turned on or off at the request of the CS by means of an "Alter Parameters" mailbox operation (see Appendix A). .ifi l1h "Control Table Interpreter" .ifi hit "K|interpreter" .ifi hit "K|op block" .ifi hit "K|terminal information block (TIB)" The interpreter is the program that performs the operations described by the "executable" portion of the control tables, i.e., the op blocks. Whenever the interpreter is invoked on behalf of any channel, it uses the TIB whose address is in index register 1; t.cur in this TIB contains the address of the first op block to be executed, which must be a wait op block. (See Section 10 for a description of the TIB.) The interpreter passes over the specified op blocks, either proceeding from one to the next or "branching" elsewhere in the tables, depending on the op blocks being processed, until another wait or a waitm op block is encountered. At this point, t.cur is updated to point to the new block (wait) or left as it was on entry (waitm), and the interpreter returns to its caller. The individual op blocks and their effects are described later in this section. .spb 2 The interpreter may be entered at any of four different entry points, representing four different types of event: timeout, output, test-state, or status. These four event types are discussed in somewhat more detail below. .ifi l2h "Timeout" .ifi hit "K|timeout" A timeout occurs when a timer started by a setime op block runs out. A timer interrupt is processed by the scheduler (see Section 11), which causes the interpreter to be invoked at the entry point itime. Op block execution begins at the first branch address specified in the wait block pointed to by t.cur; if the address is zero, the interpreter returns without doing anything. If a timeout address is specified in a wait block, the block should be preceded by a setime op block, so that a timeout is in fact possible. .ifi l2h "Output" When output for a channel arrives from the CS, and output is not currently being sent to that channel (as indicated by the TIB flag tfwrit), dia_man calls the interpreter entry point iwrite to start sending output to the channel. (See the discussion of dia_man in Section 13.) Op block execution begins at the second branch address specified in the wait block pointed to by t.cur; if the address is zero, the interpreter returns without doing anything. .ifi l2toc "Test-state" .ifi l2exact "T__e_s_t-__s_t_a_t_e" .ifi hit "K|write control data (WCD)" When the CS sends a submailbox containing one of a variety of WCD operation codes (see Appendix A) intended to change the state of a channel, dia_man turns appropriate TIB flags on or off and calls the interpreter at the entry point itest. Op block execution begins at the third branch address specified in the wait block pointed to by t.cur; if the address is zero, the interpreter returns without doing anything. The normal action by the control tables at a test-state branch is to test any TIB flags that are of interest in view of the current state of the channel. .ifi l2h "Status" .ifi hit "K|status" When hsla_man or lsla_man detects any of several kinds of change in the status of a channel, it calls the interpreter at the entry point istat. A word describing the latest status of the channel is passed in the A register. The kinds of status change reported in this manner include changes in data-set status, the receipt of a break character (as defined by either the break list or the CCT), and various kinds of software status generated by contrl or cmd op blocks. The meanings of the individual bits in the status word are described later in this section. .ifi hit "K|break character" .spb 2 .brn 8 When the interpreter is called at istat, it examines the op blocks immediately following the wait block pointed to by t.cur; in general, each wait block is followed by one or more status blocks. Each status block contains a word of "on" bits, a word of "off" bits, and a branch address. A status block is "satisfied" by the status word passed in the A register if all the 1-bits in the "on"-word of the status block are on in the status word _a_n_d all the 1-bits in the "off"-word of the status block are off in the status word. (Zero bits in the status block are ignored.) The status blocks are examined in order to see if any of them are satisfied by the supplied status word; as soon as one is found that is satisfied, op block execution proceeds starting at the branch address specified in the satisfied status block. If the interpreter finds a nonstatus block before finding any satisfied status blocks, it returns without doing anything (the status is effectively ignored). .ifi l1h "Status And Control Bits" The meanings of the bits used in status op blocks, status words passed to istat, and cmd and contrl op blocks are described below. These bits are defined by the csbits macro; the tconst macro defines the alternate forms of their names that are normally used in control tables. .ifi l2h "Status Bits" .ifi hit "K|automatic call unit (ACU)" .ifi hit "K|binary synchronous" Bits 0-3 each have two different meanings: one for a channel connected to an automatic call unit (ACU), the other for a binary synchronous (bisync) channel. .spb 2 .tlh 1 |B__i_t__p_o_s_i_t_i_o_n M__a_s_k N__a_m_e M__e_a_n_i_n_g||| .hla 1 |B__i_t__p_o_s_i_t_i_o_n M__a_s_k N__a_m_e M__e_a_n_i_n_g||| .hla 2 .inl 40 .unl 40 0 400000 ads ACU: raised data set status .spb .unl 40 bscdmk bisync: delayed marker .spb .unl 40 1 200000 acr ACU: abandon call and retry .spb .unl 40 bscmrk bisync: marker status .spb .unl 40 2 100000 dlo ACU: data line occupied .spb .unl 40 .inl 37 .unl 37 bscrcr bisync: receive block termination .spb .brn 10 .unl 37 3 040000 pwi ACU: power indicator .spb .unl 37 rcvto bisync: receive timeout .spb .unl 37 4 020000 xte transfer timing error .spb .unl 37 5 010000 parity parity error .spb .unl 37 6 004000 exh "exhaust" status: indicates that absolute input buffer limit has been reached for the channel .ifi hit "K|exhaust status" .spb .unl 37 7 002000 ring ring indicator .spb .unl 37 8 001000 brkchr break character received .spb .unl 37 9 000400 break line break received (e.g., INTERRUPT button pressed) .spb .unl 37 10 000200 prexh "pre-exhaust" status: indicates that initial input buffer limit has been reached for the channel .ifi hit "K|pre-exhaust status" .spb .unl 37 11 000100 term terminate status (generated by control tables) .ifi hit "K|terminate status" .spb .unl 37 12 000040 marker marker status (generated by control tables) .ifi hit "K|marker status" .spb .unl 37 13 000020 st status requested (generated by control tables) .spb .unl 37 14 000010 suprec supervisory receive .spb .unl 37 15 000004 dsr data set ready .spb .unl 37 16 000002 cts clear to send .spb .unl 37 17 000001 cd carrier detect .hla .spb 2 .fin .inl 0 Bits 11-17 correspond to bits 11-17 of the TIB status word, t.stat. .brp .ifi l2h "Control Bits" These bits are specified in the contrl op block or in the cmd sub-operation of the dcwlst op block. (The contrl macro generates a dcwlst op block consisting of one cmd sub-operation. See the descriptions of the individual op blocks later in this section.) A cmd sub-operation usually contains one word of control bits, but may optionally contain a second word. .ifi l3h "First Word" .tlh 1 |B__i_t__p_o_s_i_t_i_o_n M__a_s_k N__a_m_e M__e_a_n_i_n_g||| .hla 1 |B__i_t__p_o_s_i_t_i_o_n M__a_s_k N__a_m_e M__e_a_n_i_n_g||| .hla 2 .inl 40 .unl 40 0-2 100000 identifies sub-operation as CMD .spb .unl 40 3 040000 not used .spb .unl 40 4 020000 rrts reset request-to-send .spb .unl 40 5 010000 srts set request-to-send .spb .unl 40 6 004000 strm send terminate status (reflected as "term" in subsequent status) .ifi hit "K|terminate status" .spb .unl 40 7 002000 smark send marker status (reflected as "marker" in subsequent status) .ifi hit "K|marker status" .spb .unl 40 8 001000 sbrk send line break to terminal .spb .unl 40 9 000400 stat send status to control tables (reflected as "st" in subsequent status) .spb .unl 40 10 000200 rsup reset supervisory transmit .spb .unl 40 11 000100 ssup set supervisory transmit .spb .unl 40 12 000040 rdtr reset data terminal ready .spb .unl 40 13 000020 sdtr set data terminal ready .spb .unl 40 14 000010 rxmit reset transmit mode .spb .unl 40 15 000004 sxmit set transmit mode .unl 40 .spb 16 000002 rrec reset receive mode .spb .unl 40 17 000001 srec set receive mode .hla .inl 0 .brf .brn 20 .ifi l3h "Second Word" .tlh 1 |B__i_t__p_o_s_i_t_i_o_n M__a_s_k N__a_m_e M__e_a_n_i_n_g||| .inl 40 .unl 40 0-2 500000 identifies second word of CMD sub-operation .spb .unl 40 3-15 not used .spb .unl 40 16 000002 rcrq reset call request (ACU) .spb .unl 40 17 000001 scrq set call request (ACU) .fin .inl 0 .ifi l1h "Op Blocks" .ifi hit "K|op block" The individual op blocks used in the control tables are described below. For each op block, the syntax of the corresponding macro is given, followed by a description of its effect and a schematic representation of the generated code. .ifi l2h "TIB Extension Addressing" .ifi hit "K|terminal information block (TIB)~extension" Each TIB may have a TIB extension associated with it. This extension is dynamically allocated and freed with the getext and retext op blocks. The extension may be used to hold either character variables, word variables, or both. Each variable type has its own addressing conventions. Character addresses are values in the range 460(8) to 477(8). These addresses correspond to the first 16 characters (8 words) of the TIB extension. There can be at most 16 TIB character variables, and all must be located in the first 8 words of the TIB extension. Word addresses are in the form of a negative word offset: the first word of the extension is -1, the second -2, etc. TIB character variables are used by op blocks such as setchr and inscan. TIB word variables are used by op blocks such as setlcl, tstlcl, etc. .spb Defining the format of the TIB extension can be simplified through the use of the tibex macro. This macro will equate a label to the next available word or character address and keep a tally of words used. The general format is: .spb tibex