Using the Interlisp-D  RS232  Facility

File:		<lispusers>RS232.tty
Created:	Jan 10, 1983
Revised: 	Feb 21, 1983, and August 18, 1983, by JonL White


              Basic RS232 Facililty Support Functions

    Through a special circuit board plugged into the parallel port of a 
Dolphin, serial RS232 communications may be interfaced using the following
basic functions.  As characters arrive in at the interface port, they are 
stored in a ring buffer (and the output functions will buffer their data in a 
ring buffer until it is full, or until explicit request is made to force it 
out).  Since there is no microcode support for buffering the characters as they
come in, there are some limitations using this facility -- primarily that the 
user has to call one of the functions which will update the input ring buffer 
at intervals frequent enough to insure getting all the characters.

  RS232INIT:  before using the RS232 facility, it is necessary to install
    certain parameters in the INS8250 chip on the abovementioned board; the
    four arguments to this function correspond to the desired Baud rate
    (150, 300, 600, . . .  9600 are supported), the number of bits per serial
    character (i.e., 7 or 8), whether or not to use the 8th bit as a parity
    bit (and if so, whether parity is to be odd or even), and the number of
    "stop" bits (except in unusual cases, 1 the default value, is used here).
    CAUTION: the value of RS232INIT as a global variable is used by these
    driver functions;  do not reset it at any time.
        A global variable, RS232XON\XOFF?, if non-NIL, causes the driver
    functions to look for ↑S on the incoming side, and to "gag" the output
    transmitter until a subsequent ↑Q has been received;  it will probably
    cause undue trouble to set this flag to true if the corresponding host
    doesn't also obey the XON-XOFF protocols.  This value is, of course,
    temporarily turned off by the FTP protocols, which transmit and receive 
    random bytes.

  RS232CLEARBUFFER:  one argument, typically one of (IN OUTPUT BOTH)
    the corresponding ring buffer is cleared (and the data lost); also, return
    will be delayed until any character currently being sent out by the INS8250
    chip has been fully transmitted (this way, not only is the ring buffer 
    empty, but so is the one-character buffer in the INS8250).

  RS232FORCEOUTPUT:  no arguments.  Ensures that all data in the output
    ring buffer is actually transmitted "on the lines".  One use of this
    function is to ensure that all data are out -- it won't return until
    this is true [but also see RS23BACKGROUND below].

  RS232BACKGROUND: one argument, "state"
    The "state" argument must be among {OFF, INPUT, OUTPUT, BOTH, ON};  except 
    for input of OFF, this turns on a background low-level process which will 
    service the UART at least once every 16 milliseconds and/or flush out the 
    output buffer.  A "state" of OFF shuts this background activity off.  A 
    "state" of INPUT  causes only the  input buffer to be serviced; OUTPUT for 
    only the output buffer, and either ON or BOTH for both buffers.  A period 
    of 16 milliseconds for the input service time should give the appearance i
    of asynchronous buffering, without dropping any characters, when used at 
    speeds of less than about 600 baud.

  RS232PEEKBYTE: no arguments.  Returns the next character sitting in the 
    input ring buffer, if any; the hardware port is checked to see if any 
    input characters are waiting, and if so they are put into the ring buffer 
    first.  Calling this function is a good way to insure that characters
    are moved, in a timely fashion, from the chip to the input ring buffer.

  RS232READBYTE: two optional arguments.  This is the basic input function,
    which will return a fixp of up to 8-bits in length.  If no character is
    available, it will return NIL; but if the first argument is a fixp, then 
    it will wait up to that many time units before returning (possibly getting
    an incoming character in the meantime); if the first argument is any
    other non-NIL value, the it will wait (possibly forever) until some
    byte comes in to be returned.  The second argument determines the
    length of a "time unit";  default is milliseconds, but alternatives are
    SECONDS and TICKS (which is the internal Dolphin clock unit -- see the 
    documentation of DURATION).  As with RS232PEEKBYTE, any call to this 
    function will update the input ring buffer as its first action.

  RS232READWORD:  arguments as in RS232READBYTE.  If two bytes can
    be read in the alloted time, they are composed into a "word"; the
    first byte comprises the high-order 8 bits of a 16-bit word, and the
    second byte comprises the low-order 8 bits.

  RS232READLINE:  three optional arguments.  A sequence of characters
    is read, until an End-of-Line character is received; all the characters
    except the EOL are returned as a string.  The first two optional arugments
    are interpreted exactly as the two optional arguments to RS232READBYTE; 
    that is, if the expected EOL is not seen "in time", then NIL is returned.
    However, if the third argument is supplied, it must be a string pointer,
    and it will be re-used to return the characters accumulated so far,
    even if there is a timeout;  note that the "characters accumulated so
    far" are merely sitting in a local "RS232READLINE" buffer, so
    succesive calls will reuse that buffer.  One other caveat:  up to 8
    character times are dallied after receiving the EOL, to see if it
    is followed by a line-feed, and if so, the line-feed is flushed.

  RS232READSTRING: six arguments, most optional.
        (#chars.limit? stopcode? noblocksflg wait? timerUnits oldstrbuffer) 
    This function will take input bytes from the RS232 port until one of three
    conditions obtains.  (1) the total number of characters taken in by this 
    call is equal to "#chars.limit?"  [NIL means no limit]; (2) a character is 
    read with character code equal to the argument "stopcode?" [NIL means no 
    limiting charcter]; or (3) an interval of time greater than that specified 
    by "wait?" has passed with no bytes available at the port.  If "wait?" is 
    non-null,  it must be an integer, and  "timerUnits" specifies the  units 
    (see section 14.6 of new manual "Timers and Duratin Functions).  i
        If "noblocksflg" is non-null, then RS232READSTRING will consume all 
    the CPU cycles without offering to yield to other processes [including the 
    MOUSE process]; this mode is important to very-time-critical applications.
    If "oldstrbuffer" is supplied, it must be a string and the result 
    characters are smashed into it [so that no consing is done].

  RS232WRITEBYTE: one argument required, one optional.  An 8-bit byte is sent
    out;  actually, if the second arg is NIL, it will just be stored in the 
    output ring buffer, and will be forced out if the buffer starts to get
    full.  Additionally, the ringbuffer will be forced out if the second 
    argument is non-NIL (or whenever there is an explicit call to the function
    RS232FORCEOUTPUT, or from time to time when RS232BACKGROUND has specified
    background output from the buffer -- see documentation above.).

  RS232WRITECHARS:  one argument required, one optional.   First argument is
    either a litatom or string, and all the characters therein are "written";
    second argument is interpreted the same as with RS232WRITEBYTE.

  RS232SENDBREAK: one optional argument.  The out-of-band BREAK signal is
    transmitted for a period of 0.25 seconds;  if the optional argument is
    non-NIL, then the period is extended to 3.5 seconds.


  RS232MODEMCONTROL: one argument, "signalslst".  A NoSpread function which 
    sets the modem control lines to be "on", for the signals in the list 
    "signalslst".  Returns the former setting of the lines.  If "signalslst"
    is not supplied [which is not the same as supplying NIL], then the control 
    lines are merely returned.  The entries in "signalslst" are litatom names 
    for standard modem control lines.   Current signal names usable are DTR 
    and RTS.

  RS232MODIFYMODEMCONTROL: two arguments "signalsonlst" and  "signalsofflst"
    Changes only those modem control lines specified in the union of the two 
    arguments; those in "signalsonlst" are set to be on, and those in 
    "signalsofflst" are set off.  Returns the former state just as 
    (RS232MODEMCONTROL) does.

  RS232MODEMSTATUSP: one argument,  "booleanform"
    Returns non-null iff the reading of the modem status lines is consistent 
    with the form "booleanform" [modem status signals currently supported are 
    CTS, DSR, RI, and RLSD].  "booleanform" may be any AND/OR/NOT combination 
    over the signal names.  Example: (RS232MODEMSTATUSP '(AND CTS (NOT RLSD))).

  RS232MODEMHANGUP: no arguments
    Takes whatever steps appropriate to cause the modem to "hang up" [mostly, 
    this means turning the DTR signal down for about 3 seconds, or until the 
    DSR signal has gone down].



    The {RS232} device is created by RS232INIT; one can obtain a stream 
interface to the RS232 port by calling (GETSTREAM '{RS232} <direction>).  
However, in most cases, this stream approach will not work unless the 
asynchronous buffering mentioned above is successful -- the time taken by 
general I/O operations is unpredictable and often quite large.  

    The global variable RS232XON\XOFF?  controls whether or not these driver
functions will participate in an XON/XOFF protocol; when non-NIL, the
any incoming XOFF character (the ↑S of ascii) will cause the output functions
to "hang" until a releasing XON character has come in (the ↑Q of ascii).
Also the global variable RS232XOFF? will reflect whether or not the the
port is currently in the "hanging" state.

    The hardware will detect the usual error conditions (dropped characters,
parity errors when so initialized, and framing errors) in addition to detecting
a BREAK being sent.  When a BREAK has been detected, the software will set the
global variable RS232BREAKSEEN? to non-NIL; it will also check the value of 
RS232BREAKFN, and if non-NIL, will apply it to NIL.  Similarly, if there is 
any error condition which causes a character to be dropped, the software will 
apply the value of RS232LOSTCHARFN to a litatom describing the reason for the 
lossage;  the default value for RS232LOSTCHARFN is \RS232DING, which will 
"flash" the display screen a couple of times, and put the value of 
\RS232.DROPPEDCHARACTER.CODE into the ring buffer [initially this is set to 
(CHARCODE #↑G)].  RS232LOSTCHARFN must, at all times, be a runnable function.




                    RS232CHAT  Facility

    The function RS232CHAT, with four optional arguments, initiates a full-
duplex transmission throught the RS232 port.  The first argument is coerced 
into a stream for printing the received characters (default is to use the 
window in the value of \RS232CHATWINDOW, which if null, will interactively ask 
the user to lay out a region for such window); second argument, if non-null, 
is a user-programmable interface for filtering the characters which arrive 
from the remote correspondent -- it must be the output of the function 
MAKEBINHOOK [as of August 18, 1983, this facility isn't quite ready -- it's 
primary application will be to provide a flexible means for emulation of the 
various semi-smart terminals like the Heath-19 etc.]; third argument, if not 
null, specifies that local echoing of the typed-in characters is to be done, 
with T meaning to use the same stream as the first argument, and any other 
value being coerced into a stream to use for local echoing; fourth argument
is whether or not to use the XON\XOFF protocol.  
    While in "chat" mode, character interrupts are shut off, the keyboard is 
rather plainly interpreted, and characters typed in on it are sent to the 
correspondent.  Ordinarily, a host will send a CR/LF for "newline", but some 
send only one;  the menu selection lets you pick one or the other if this is 
the case (especially useful with UNIX systems).  Similarly, you can specify 
that the RETURN key (or, EOL key) send either just CR or both CR/LF.  If local
echoing is being performed, and it the local echo stream is the same as the
main RS232CHAT window, then the locally-generated characters will be enclosed
in square-brackets, as a means of distinguishing local echo from remote output.

    Caveat: Output to the the Dolphin display takes a non-trivial amount of 
time (e.g., just going through the character printout routines, and "painting" 
a character onto the screen bit-map  requires over a millisecond; scrolling
a modest-sized window may take well over 30 milliseconds).  Without additional
microcode support, to maintain the input ring buffer asynchronously, it is
questionable whether rates above 2400 baud will be acceptable for RS232CHAT, 
and there may be ocasional problems above 1200 baud).  At "slower" speeds,
that is, at less than about 600 baud, the use of RS232BACKGROUND may alleviate
these problems.  However, RS232CHAT will pay attention to the DSPSCROLL setting
of the chat window, and will do "roll" mode rather than "wrap" mode provided 
that it can do so without dropping characters ["roll" describes the "scroll up"
action when typeout reaches the bottom of the window].  If the XON/XOFF 
protocol is being used, or if the background process mentioned above is in 
operation, then likely there will be no problem is using "roll" mode.

    Escape from "duplex" mode is made by typing the the character which is
found in the value of \RS232ESCAPE.CHARCODE, currently initialized to
(CHARACTER #B) [this happens to be middle-blank].  Typing "?" just after
the escape character will give a small "help message; the commands to be used 
in this mode are all one-letter:
   B - send a BREAK  (0.25 seconds)
   E - change the escape character
   F - deactivate the XON\XOFF protocol
   H - call the function (HELP), with interrupts re-activated
   L - call the function (RS232.PROMPT&LOGIN)
   O - set the XON\XOFF protocol active
   Q - for quit and exit, presumably back to LISPX
   S - set the speed of the RS232 port; ? will display choices.
   <CR> - 'Return' key sends <CR> to remote host
   <LF> - 'Return' key sends <CR><LF> to remote host
   ↑B - run a "break" or HELP loop
   R  - call RAID
   7 - truncate incoming characters to 7 bits (this is necessary when you
        have opened an 8-bit connection ignoring the parity bit; you really
        only want to see the lower 7 bits interpreted as a character to be
        printed on the window).
   8 -  undo the "7-bit" mode above (just in case you actually wanted to
        see the eight bit -- typically the printout will be just the same as
        as the 7-bit printout, but preceeded by a "#" when the eighth bit
        is on in a character.)

    Additional control may be exercised with the pop-up menu obtained by
pressing the middle mouse button with the cursor in the RS232CHAT window
while RS232CHAT is active; its commands are essentially self-documenting,
and are a super-set of the above-mentioned commands available from the
keyboard.  In particular, it's possible to alter what RS232CHAT thinks is
the "NewLine" character; Interlisp-D's default is to choose CR, but for
connections to some systems, LF is a much better choice.  
    Two other commands permit "toggling" (that is, switching the state from one
choice to an alternate, and vice-versa): ~LocalEcho and ~RollMode.  The latter
will change the DSPSCROLL of \RS232CHATWINDOW; the former will "toggle" the use
of the local echo stream provided in the call to RS232CHAT (or will use 
\RS232CHATWINDOW if no stream was provided).

The RS232CHAT window tries to play the "TTY-process passing" protocol described
in the recent documentation for multiple-process and TTY interactions.

    A number of variables control certain characteristics; in addition
to \RS232ESCAPE.CHARCODE mentioned above, there are:

  \RS232PERMITTED.INTERRUPTS -- a list of items such as returned by
      INTERRUPTCHAR (or such as would be input to RESET.INTERRUPT), which will 
      be "active" during RS232CHAT; initially this list is null.
  \RS232CHAT.IgnoreCharcodes -- a list of character codes that will be ignored
      by the input side of RS232CHAT; initially this list contains only the
      single code (CHARCODE NULL).
  \RS232CHAT.EOLsequence -- A string of characters to be sent out whenever the
      RETURN key is typed on the keyboard; initially this just contains the one
      character CR, and is changeable by a menu command.
  \RS232CHAT.NEWLINECHAR -- Normally set to LF, which will cause RS232CHAT
      to work right for systems which send both CR/LF for newline as well as for
      those that send only LF (e.g., UNIX).  However, some hosts send only CR,
      and thus to get RS232CHAT to advance to a new line, CR must be recognized
      as the "newline" character.  This option is changeable by a menu command.  Note
      that this section isn't talking about  the EOL character.
  \RS232CHAT.BellSequence -- Since the standard Interlisp-D action for printing
      a "bell" to a display stream takes too long (much longer than the inter-
      character time at 1200 baud), then unless the XON\XOFF protocol is
      active, this string of characters will be substituted for the 
      (CHARCODE BELL).  Initially, this is  "↑<bell>".

    The following two functions are most useful when trying to "chat" to a
host through an RS232 connection running at a speed higher than Interlisp-D
can support for display stream activities:

  RS232LOGIN: 6 arguments, most optional.  First is the name of a host
    machine with which the RS232 port is corresponding, second is the
    desired username/login.id on that machine,  third is the password
    needed there, and fourth is the "host system type"; the remaining two
    arguments are concerned  where to echo the activity caused by this
    function, and are mainly of interest to other system-level functions.
    If either "username" or "password" is NIL, the will be obtained via
    PROMPTFORWORD from the keyboard (this is so that you don't have to have
    passwords in code files); there is also an internal cache of the
    information about host/username/password, just as is kept for logins
    over the Ethernet.  [see documentation of PROMPTFORWORD]
        When the Host's system type is known, then a database of login
    protocols is consulted to figure out how to send (automatically and 
    blindly) the necessary characters to effect a login.  At convenient
    moments, the output from the host, which is accumulated in the RS232 
    line buffer, will be output for the user's perusal (the fifth argument
    is a stream for this printout: NIL defaults to the primary output,
    NONE gags this type-out;  window, files ets. all are acceptable here).
    A primary reason for this function's existence, besides the cacheing of
    such information as login.id and password, is to permit loggin in at
    speeds which cannot support RS232CHAT (see documentation below).

  RS232.PROMPT&LOGIN: one argument.  Prompts the user (via PROMPTFORWORD) to 
    type in the necessary information, in the PROMPTWINDOW, to call and use
    RS232LOGIN; the argument is handed to RS232LOGIN as its fifth argument
    (the "Type-out stream" argument).



                    RS232 "FTP" Facility

    Two functions exist for interfacing to a new protocol for doing file 
transfers over an RS232 connection; the primary version of this new protocol 
was developed in the micro/home computer world, where there was a need to 
transfrer files between a "home" computer and some major, RS232 accessible 
host.  Since the RS232 connection was most often made through a telephone 
modem, this protocol has come to be known as MODEM; unfortunately, since CP/M 
was the predominant operating system on these "home" computers, the protocol 
does not provide a totally secure way of knowing how long a file really is;
furthermore, the packet size is fixed at 128 bytes, and some systems have
input buffers for which this is frequently too large.   [But I have some variants
on this protocol which solve these problems, and I intend to certify their
implementation in Interlisp and suggest them to the other MODEM users].
Nevertheless, the protocol does have packetizing, checksumming, and timeouts;
so there is only a very small probability that a file so transmitted will have
undetected errors.

  RS232GETFILE: three required arguments, and one optional.  First argument
    is the name of a file to store the file being transmitted *from* the 
    correspondent; second arg is either TEXT or BINARY (ASCII permissible 
    in place of TEXT), indicating the file type (in the Interlisp-D sense); 
    third argument is the protocol being used (currently, only MODEM is 
    acceptable here); fourth argument, when given is just transmitted first 
    (typically, this would be the series of characters you would type at the 
    remote executive to cause the desired file to be run).
  RS232PUTFILE: arguments the same as for RS232GETFILE, except that the 
    direction of transmission is from the existing file on the Dolphin *to* 
    the correspondent.

Examples: (assuming connection to a TOPS-20 host)

   (RS232GETFILE  '{DSK}MUMBLE  'TEXT  'MODEM  
          "MODEM SA <LISPUSERS>MUMBLE
")

   (RS232PUTFILE  '{DSK}RUN.DCOM  'BINARY  'MODEM  
          "MODEM RB <JONL>RUN.DCOM
")

Both ends of the MODEM protocol have "synchronizing" features, so
a typical scenario of usage would be to use RS232CHAT to login to
the host, and then simply put the MODEM program in its wait state,
by typing whatever arguments it needs, and finally exiting from
RS232CHAT and calling RS232GETFILE (or RS232PUTFILE) directly,
without the fourth argument.   The fourth-argument facility is
provided so that one may use RS232FTP at speeds greater than would
be available for RS232CHAT; login could thus be achieved through
use of the function RS232.PROMPT&LOGIN.

    The global variable RS232FTPTRACEFLG, if non-null, causes a trace
of activity to be printed out on the file/stream specified by the global
variable RS232TRACEFILE;  it the value is PEEK, then only a "+" will
be printed for successful transit of packets, and "-" for unsuccessful ones;
any other non-null value causes a more verbose output.


Several implementations of the MODEM protocol for other machines are available:
one for the IBM/PC is available on floppy disk through XSIS (and is also on 
[MAXC]<XEOS>IBMFTP.ASC).  Several files are available also on [MAXC]<XEOS> for 
VAX/VMS users: XMODEM.FOR and QIO.DCK are an implementation in FORTRAN; 
TOXMOD.FOR and FMXMOD.FOR are helpful for dealing with the structure of files 
in VMS's record management system.