[comp.os.vms] CMU pseudo-terminal drivers

KVC@ENGVAX.SCG.HAC.COM (Kevin Carosso) (07/17/87)

$ show default
$ check_sum = 1515454846
$ write sys$output "Creating 000_README.TXT"
$ create 000_README.TXT
$ DECK/DOLLARS="$*$*EOD*$*$"
These files make up a pseudo terminal driver for VAX/VMS.  This
driver runs under VMS version 4.x.  It will not work under VMS prior
to 4.  See PSEUDO.DOC for documentation and NOTES.TXT for my
additional comments and observations.

This new version fixes up several major bugs, including:

  o  ^S^Y system crasher
  o  randomly munged <LF> and <ESC> characters
  o  SHOW DEVICE stack dumps
  o  system crashes due to attempted use of the TPA0: device
  o  terminal device name changed from PTAn: to TPAn: because
     VMS uses PTA for MSCP tapes.

This driver was originally developed at Carnegie-Mellon University and has
made the rounds before as the CMU PTY driver.

        /Kevin Carosso              kvc@engvax.scg.hac.com
         Hughes Aircraft Co.    or  kvc%engvax@oberon.usc.edu

         December 9, 1986
$*$*EOD*$*$
$ checksum 000_README.TXT
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 1780435178
$ write sys$output "Creating DESCRIP.MMS"
$ create DESCRIP.MMS
$ DECK/DOLLARS="$*$*EOD*$*$"
! Build the pseudo-terminal utility, PYDRIVER and TPDRIVER.
!
INCLUDE pub_root:[lib.mms]include.mms

DESTDIR = $(PRIVDIR)
distribution_files = 000_readme.txt, descrip.mms, driver.opt, notes.txt, -
                     pseudo.doc, pty.pas, pydriver.mar, tpdriver.mar
MFLAGS = /NOLIST

build : PYDRIVER.EXE TPDRIVER.EXE
        @ WRITE SYS$OUTPUT "Build finished."

install : build $(DESTDIR)pydriver.exe $(DESTDIR)tpdriver.exe
        $ write sys$output "Installation finished"

distribute : zz_pty_dist.com
        $ write sys$output "DCL archive complete: $(MMS$SOURCE)"

TPDRIVER.EXE : TPDRIVER.OBJ
        - LINK TPDRIVER.OBJ,DRIVER.OPT/OPT,SYS$SYSTEM:SYS.STB/SELECT

PYDRIVER.EXE    : PYDRIVER.OBJ
        - LINK PYDRIVER.OBJ,DRIVER.OPT/OPT,SYS$SYSTEM:SYS.STB/SELECT

$(DESTDIR)pydriver.exe : pydriver.exe
        $ copy $< $@

$(DESTDIR)tpdriver.exe : tpdriver.exe
        $ copy $< $@

zz_pty_dist.com : $(distribution_files)
        $ @pub:archive $(MMS$TARGET) "$(MMS$SOURCE_LIST)"
$*$*EOD*$*$
$ checksum DESCRIP.MMS
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 1702060495
$ write sys$output "Creating DRIVER.OPT"
$ create DRIVER.OPT
$ DECK/DOLLARS="$*$*EOD*$*$"
base=0
$*$*EOD*$*$
$ checksum DRIVER.OPT
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 238513379
$ write sys$output "Creating NOTES.TXT"
$ create NOTES.TXT
$ DECK/DOLLARS="$*$*EOD*$*$"
These notes should be considered an addition to the documentation
to be found in PSEUDO.DOC.  These notes document some enhancements
I have made to the drivers as well as some caveats I have found.

First of all, I want to grant credit to the original authors of the
driver, they are listed at the beginning of the two source files.  In
private correspondence with Dale Moore at CMU, he has chosen to release the
driver to the public domain.  There have been various incarnations of this
driver in the past.  I started with a version that was modified to run
under VMS V4 and cleaned it up and made CONNECT/DISCONNECT and HANGUP work
consistently.  I also changed the device name of the terminal device from
PTAn: to TPAn: because VMS now uses PTA0: for MSCP tapes.

The drivers have been tested on VMS V4.0 through V4.5.

Load the two drivers as PYA0: and TPA0:.  Assigning a channel to
PYA0: will create a PYAn: and a TPAn: pseudo-terminal pair (the unit
numbers just go up).  Anything you write to that channel appears on the
TP as if typed at a terminal.  Any output to the terminal TPAn: is available
to be read from PYAn:.  On output, the devices do flow control between one
another and all buffering.  You can just read, for example, with a 100
char buffer from PY and the read will complete with whatever number of
characters the QIO to the TP terminal had, anywhere from 1 to MAXBUF.
If the QIO to the TP had more chars than your read buffer, it'll just
fill the read buffer and return, and you can get the rest on the next
read.  On input, it is possible to overrun the TP device's terminal
type-ahead buffer, as it is with a real terminal.

If you assign a mailbox with the PYAn: device (use LIB$ASN_WTH_MBX
when assigning the channel to PYA0:) you will get an MSG$_TRMHANGUP
message in the mailbox whenever the terminal is hung-up.  This occurs
when a process deassigns the last channel to the device, just as a HANGUP
will on terminals with modem signals.  You may ignore the hangup message if
you choose and continue to use the device after the message.

Should you deassign all channels to the PYAn: device, a hangup will be
forced on any process using the TPAn: device.  Just like dropping carrier on
a modem line.  If the pseudo-terminal is in use by an interactive process
and has an associated virtual terminal, the process will be disconnected,
otherwise the interactive process will be logged out.

To enable virtual terminals on pseudo-terminals, you must have the
TT2$M_DISCONNECT bit set in the system default terminal characteristics
(TTY_DEFCHAR2 in SYSGEN).

Note that, just as with normal VMS terminals, you will only get a
VTAn: if the line has the DISCONNECT terminal characteristic before
you log into it, and only if you use LOGINOUT to start a process on
the terminal.  Sending a <CR> into the pseudo-terminal device will
start up LOGINOUT as it would on a real terminal.

There is currently one minor known bug in the drivers, a side-effect of
the driver being cautious in order to prevent possible problems.  If you
have a virtual terminal associated with the pseudo-terminal, and you
deassign the last channel to the control device (the PY device) then a
DISCONNECT is forced on any process on the TP and both devices should be
deleted.  Currently, the TP will not disappear, though the PY will and
the TP will be marked as OFFLINE.  This is because I rely on VMS to do
the actual delete of the device and it appears that virtual terminals
screw up the way device reference counts work, so VMS never gets around to
deleting the device.  It is marked for delete, so anything you do (I like
SHOW TERM TPA53:, for example) will cause it to disappear.  I could fix this
by zapping the device explicitly after forcing the DISCONNECT, however I
am not convinced that someone, somewhere, won't try to reference the UCB and
thus cause bad things to occur.  Note that it DOES get deleted correctly if
you do not have a virtual terminal associated with the TP or if you have no
processes active on the TP when the PY and TP are to be deleted.  These two
cases are generally the norm.

I am currently considering a few enhancements:

 o  It should be possible to set the TP terminal characteristics through
    the PY device, so the controlling program can set them.

 o  The TP device driver should notify the controlling program (via the
    associated mailbox) whenever the TP terminal characteristics are
    changed in case applications need to know such things.

 o  There should be a mechanism, again utilizing the associated mailbox,
    for the controlling application to be notified when the TP's typeahead
    buffer fills.

I need to think about the ramifications of changing device characteristics on
TTDRIVER unexpectedly, however, before I make the first change.

If you need to set characteristics of the terminal device right now,
you must assign a channel to the TP device and use a SETMODE QIO
then deassign the channel before giving the TP to another process. You
will get a HANGUP message in the associated mailbox when you
deassign if the HANGUP attribute was set on the device.  You can
ignore this message, however, and go ahead and use the device.

You cannot set characteristics of the master device.  This used to be
allowed, though it had no purpose, and it crashes the system (at
least under V4).

        /Kevin Carosso               kvc%engvax.UUCP@oberon.usc.edu
         Hughes Aircraft Co.

         December 9, 1986
$*$*EOD*$*$
$ checksum NOTES.TXT
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 1110109988
$ write sys$output "Creating PSEUDO.DOC"
$ create PSEUDO.DOC
$ DECK/DOLLARS="$*$*EOD*$*$"


1. PTY Driver
  This  chapter  describes  the  use  of  the VAX/VMS Pseudo Terminal Driver as
implemented Carnegie-Mellon University Computer Science Department.  Additional
information about VMS device drivers and the VMS operating system interface can
be found in VAX/VMS I/O User's Guide, and in VAX/VMS System  Service  Reference
Manual.

1.1 PTY Driver Features and Capabilities
  Pseudo  terminals  (or  PTYs)  are  virtual  terminals in the system.  Unlike
normal terminals where the I/O is actually done with a physical  device,  PTY's
I/O  has  no  interface to any physical device.  In this way they are much like
mailboxes.



1.1.1 Concept of Control and Terminal Device
  A single PTY consists of two devices, a control and a terminal device.    The
terminal device is the device that acts like a terminal.

  The  control  device  on  VMS is named ``PYAx:''.  For example, a PTY control
device could be named ``PYA1:''.  The terminal device portion  of  the  PTY  is
name  ``TPAx:''.    The  terminal  device  ``TPA1:'' is the mate to the control
device ``PYA1:''.  Similarly, the control device ``PYA5:'' is the mate  to  the
terminal device ``TPA5:''.


1.1.1.1 Similarities Between PTYs and Terminals
  The  terminal  portion  of  a  PTY  behaves  very much like a regular VAX/VMS
terminal.  The terminal portion of a PTY has

   - Type-ahead

   - Specifiable or default line terminators

   - Special operating modes, such as NOECHO and PASSALL

   - American National Standard escape sequence detection

   - Terminal/mailbox interaction

   - Terminal control characters  such  as  Sontrol-S  and  Control-Q  for
     starting  and  stopping  output, Control-O for discarding output, and
     all other special characters that are handled by the VAX/VMS terminal
     driver.

   - Limited  full-duplex  operation(simultaneously  active read and write
     requests)


1.1.1.2 Differences Between PTYs and Terminals
  The difference between a VAX/VMS terminal and the terminal portion of  a  PTY
is  where  the  input  comes  from  and where the output goes to.  On a VAX/VMS
terminal, the input comes from an actual terminal and the  output  goes  to  an
actual  terminal.    On  a  PTY the input comes from the control device and the
output goes to the control device.  In order to simulate someone typing at  the
terminal  device  ``TPA5:''  we must write to the control device ``PYA5:''.  In
order to read what has been typed out to the terminal device ``TPA5:''  we must
read  from  the  control  device  ``PYA5:''.  PTYs are like terminals that only
software can access.

  There is no buffering of the input on the control device.  It is possible  to
fill up the terminal device's typeahead buffer by writing large amounts of data
to the control device.

  PTYs are virtual devices.  When allocating  a  PTY,  they  behave  much  like
mailboxes or network devices.  To allocate a PTY, simply allocate the first one
('PYA0:').  The allocating routine will be create a new PTY and  assign  it  to
you.  The PTY will be deallocated when no process is referencing the device.


1.1.1.3 Uses of PTYs
  There  is  no  device  from  DEC  which suffices for remote logins when using
non-DECnet protocols.  The remote terminal driver expects the remote node to do
the local line editing.  This includes delete, control-o, control-s, control-q,
and much other stuff.  PTYs handle the local line editing locally.    When  the
line  editing  is  done locally, the remote machine has to know much less about
VMS and its terminal handling conventions.

1.2 Installing PTYs
  In VAX/VMS V3.0, several pieces of software support the  terminal  interface.
The  hardware  independent portion is SYS$SYSTEM:TTDRIVER.EXE.  This version of
PTYs does not require any changes or patches to this code.    There  are  other
pieces   of   software  for  particular  pieces  of  hardware.    The  code  in
SYS$SYSTEM:DZDRIVER.EXE is the device driver required for  DZ11's  and  DZ32's.
The  code in SYS$SYSTEM:YCDRIVER.EXE is the device driver required for DMF32's.
PTY drivers consist of  two  device  drivers,  TPDRIVER.EXE  for  the  terminal
portion of the PTY's, and PYDRIVER.EXE for the control portion.



1.2.1 Compiling Sources
  There   are  several  pieces  of  source  code  that  make  up  PTY  drivers.
PYDRIVER.MAR is the source for the control portion.  TPDRIVER.MAR is the source
for the terminal portion.

  The commands for compiling and linking the devices are:
$ ! Compile the drivers
$ MACRO /LIST /OBJECT TPDRIVER.MAR+SYS$LIBRARY:LIB/LIBRARY
$ MACRO /LIST /OBJECT PYDRIVER.MAR+SYS$LIBRARY:LIB/LIBRARY
$ ! Link the drivers
$ LINK /SHARE /MAP /FULL /CROSS -
        TPDRIVER,SYS$SYSTEM:SYS.STB/SELECTIVE,SYS$INPUT:/OPTION
BASE=0
$ LINK /SHARE /MAP /FULL /CROSS -
        PYDRIVER,SYS$SYSTEM:SYS.STB/SELECTIVE,SYS$INPUT:/OPTION
BASE=0



1.2.2 Installing the Devices
  To  load  a  PTY,  login  to  a  privileged  account  and issue the following
commands.
$ RUN SYS$SYSTEM:SYSGEN
CONNECT TPA0/NOADAPTER/DRIVER=device:[directory]TPDRIVER
CONNECT PYA0/NOADAPTER/DRIVER=device:[directory]PYDRIVER

  You may want to build a command file which installs the devices at boot  time
by modifying SYS$MANAGER:SYSTARTUP.COM.

  Don't try to use TPB0 or PYB0.  The code is not built to handle these.  It is
only built to handle the PYA and TPA control and terminal devices.

1.3 Device Information
  User processes can obtain terminal and control device  information  by  using
the  $GETDVI,  $GETCHN and $GETDEV system services (see VAX/VMS System Services
Reference Manual).

  It is recommended that new programs make use  of  the  $GETDVI  and  $GETDVIW
system services.



1.3.1 PTY Terminal Device Dependent Information
  The  information  returned  about  a terminal device is in the same format as
information returned about a regular VAX/VMS terminal.  By only looking at  the
information returned from $GETCHN and $GETDEV system services, it is impossible
to tell the difference between a PTY terminal device  and  an  actual  terminal
device.



1.3.2 PTY Control Device Dependent Information
  When  applied  to  a  PTY  control  device, $GETCHN return information in the
format as show in Figure 1-1.


              31        24 23        16 15         8 7          0
             -----------------------------------------------------
             |              Device Characteristics               |
             -----------------------------------------------------
             |       Buffer Size       |    Type    |    Class   |
             -----------------------------------------------------
             |           Unused        |     TP Unit Number      |
             -----------------------------------------------------

            Figure 1-1:   PTY Control Device Dependent Information

  The first longword contains device-independent data.  The  second  and  third
longwords contain device-dependent data.


1.3.2.1 Device Characteristics
  The  characteristics  of  the  the  PTY  control device (PYA) can be found by
either using the first longword returned by  the  $GETDEV  and  $GETCHN  system
services, or by using the item code DVI$ DEVCHAR to the $GETDVI system service.

  A PTY control device has the following characteristics.

   - DEV$M AVL - On line and available

   - DEV$M IDV - Capable of input

   - DEV$M ODV - Capable of output

   - DEV$M REC - Record oriented

  A PTY control device does not have the following characteristics.

   - DEV$M CCL - Carriage Control

   - DEV$M TRM - Terminal Device


1.3.2.2 Buffer Size
  Also returned from $GETDVI by using item code DVI$ DEVBUFSIZ.


1.3.2.3 Device Type
  The  device  type  is  DT$ PY.  Since a PTY is a nonstandard device, you will
probably not find the correct macros, literals or  constants  for  this  device
type.  This field should have a value of 0.

  Also returned from $GETDVI by using item code DVI$ DEVTYPE.


1.3.2.4 Device Class
  The  device  class  is DC$ PY.  Since a PTY seems to be such an odd creature,
this field should be FF in hex, or 177 in octal.

  Also returned from $GETDVI by using item code DVI$ DEVCLASS.


1.3.2.5 PT Unit Number
  The unit number is unit number of the associated PTY terminal device.

  The unit number is also returned as the low sixteen bits in DVI$ DEVDEPEND by
$GETDVI.

1.4 PTY Function Codes
  The  function  codes for the terminal device portion of a PTY are exactly the
same as those for regular VAX/VMS terminals.  For more information  on  VAX/VMS
terminal I/O see VAX/VMS I/O User's Guide.

  The  basic  function  of the control portion of a PTY are read, write and set
mode or characteristics.

  A user does not need to have assigned the PTY terminal device in order to  do
I/O  operations  on  the  PTY  control device.  If the terminal device has type
ahead enabled, sending the right characters at the control device will  send  a
message  to  OPCOM  to  start  running  SYS$SYSTEM:LOGINOUT.EXE on the terminal
device.



1.4.1 Read
  The basic purpose of a PTY control device read is to transfer data  from  the
output buffer of the PTY terminal device to a user specified buffer.  There are
three read functions which a user can apply to a PTY control device.

   - IO$ READVBLK - Read virtual block

   - IO$ READLBLK - Read logical block

   - IO$ READPBLK - Read physical block

  A read is complete if either of the below conditions occur:

   - The user specified buffer is full

   - At least one character is available from the PTY terminal device

  The read function codes can  take  the  following  device/function  dependent
arguments:

   - P1  =  The  starting virtual address of the buffer that is to receive
     the data read

   - P2 = The size of the buffer that is to receive the data read in bytes

   - P3, P4, P5, P6 = ignored



1.4.2 Write
  The basic purpose of a PTY control device write is to transfer data from  the
user  specified  buffer  to  the  typeahead  buffer of the PTY terminal device.
There are three write functions which a user can apply to a PTY control device.

   - IO$ WRITEVBLK - write virtual block

   - IO$ WRITELBLK - write logical block

   - IO$ WRITEPBLK - write physical block

  The write function codes can take  the  following  device/function  dependent
arguments:

   - P1 = The starting virtual address of the buffer that is to be written
     to the PTY terminal device

   - P2 = The number of bytes that are to be sent

   - P3, P4, P5, P6 = Ignored



1.4.3 Set Mode and Set Characteristics
  The Set Mode function affects the mode and temporary characteristics  of  the
associated PTY control device.  Set Mode is a logical I/O function and requires
no privilege.  A single function code is provided:

   - IO$ SETMODE

  The Set Charateristics function affects the permanent characteristics of  the
associated  PTY control device.  Set Characteristics is a physical I/O function
and requires the privilege  necessary  to  perform  physical  I/O.    A  single
function code is provided:

   - IO$ SETCHAR

  These  functions take the following device/function dependent arguments if no
function modifiers are specified:

   - P1 = address of characteristics buffer

   - P2, P3, P4, P5, P6 = ignored

  The P1 argument points to a quadword block, as shown in Figure 1-2.


              31        24 23        16 15         8 7          0
             -----------------------------------------------------
             |       Buffer Size       |    Type    |    Class   |
             -----------------------------------------------------
             |                      Unused                       |
             -----------------------------------------------------

                 Figure 1-2:   Set Mode Characteristics Buffer


1.4.3.1 Function Modifiers
  Function Modifiers to the control device currently do not affect the state or
accessibility of terminal device.

  You  cannot  change  the  mode  or  characteristics of the terminal device by
adding function modifiers to the I/O routines  for  the  control  device.    An
earlier version of PTYdrivers supported this feature.  This feature has not yet
been added to this version.



1.4.4  Sense Mode and Sense Characterisitics
  The two function codes to sense the mode of the PTY control device are:

   - IO$ SENSEMODE

   - IO$ SENSECHAR

  The IO$ SENSEMODE function returns the process-associated, that is temporary,
characteristics  of the PTY control device.  The IO$ SENSECHAR function returns
the permanent characteristics of the PTY control  device.    The  IO$ SENSEMODE
function  is  a  logical function and requires no privilege.  The IO$ SENSECHAR
function is a physical function and requires the privilege necessary to perform
physical I/O.

  These function codes take the following device/function dependent.

   - P1 = address of the quadword characteristics buffer

   - P2, P3, P4, P5, P6 = ignored

  The  P1  argument  points to a quadword block which is the same format as the
Set Mode Characteristics Buffer, shown in Figure 1-2



1.4.5 I/O Status Block
  The I/O status block formats for read, write, set mode  and  sense  mode  I/O
functions are shown in Figures 1-3 and 1-4.


              31        24 23        16 15         8 7          0
             -----------------------------------------------------
             |       Byte Count        |         Status          |
             -----------------------------------------------------
             |                      Unused                       |
             -----------------------------------------------------

             Figure 1-3:   IOSB Contents - Read and Write Function


              31        24 23        16 15         8 7          0
             -----------------------------------------------------
             |         Unused          |         Status          |
             -----------------------------------------------------
             |                      Unused                       |
             -----------------------------------------------------

Figure 1-4:   IOSB Contents - Set and Sense Mode and Characteristics Functions

  The status indicates the succes or failure of the specified operation.  Below
are possible values for the status field:

   - SS$ ABORT - The operation was canceled by the Cancel I/O  on  Channel
     ($CANCEL)  system service.  Applicable only if the drive was actively
     involved in an operation.

   - SS$ NORMAL - Successful Completion

   - SS$ ACCVIO - The specified buffer is not accessible to the  specified
     process

1.5 Possible Improvements and Bugs
  Watch  the  error counts on the devices.  Occasionally, a PTY terminal device
might get a timeout.  Perhaps,  it  is  not  executing  at  a  sufficient  IPL.
Somehow, we aren't entering the startio routine soon after the UCB$V_TIM bit is
set in UCB$W_STS.

  It may be useful for the associated mailbox of the PY  device  to  receive  a
message  every  time  the  stop,  start, abort, resume, xoff, or xon routine is
called.  This would permit programs that control the  PY  device  to  determine
when  to slow down the data rate, or abort the output in the buffers which have
already been read.



                               Table of Contents

1. PTY Driver                                                                 2

   1.1 PTY Driver Features and Capabilities                                   2
       1.1.1 Concept of Control and Terminal Device                           2
           1.1.1.1 Similarities Between PTYs and Terminals                    2
           1.1.1.2 Differences Between PTYs and Terminals                     2
           1.1.1.3 Uses of PTYs                                               2
   1.2 Installing PTYs                                                        2
       1.2.1 Compiling Sources                                                2
       1.2.2 Installing the Devices                                           2
   1.3 Device Information                                                     2
       1.3.1 PTY Terminal Device Dependent Information                        2
       1.3.2 PTY Control Device Dependent Information                         2
           1.3.2.1 Device Characteristics                                     2
           1.3.2.2 Buffer Size                                                2
           1.3.2.3 Device Type                                                2
           1.3.2.4 Device Class                                               3
           1.3.2.5 TP Unit Number                                             3
   1.4 PTY Function Codes                                                     3
       1.4.1 Read                                                             3
       1.4.2 Write                                                            3
       1.4.3 Set Mode and Set Characteristics                                 3
           1.4.3.1 Function Modifiers                                         3
       1.4.4  Sense Mode and Sense Characterisitics                           3
       1.4.5 I/O Status Block                                                 3
   1.5 Possible Improvements and Bugs                                         3



                                List of Figures
   Figure 1-1:   PTY Control Device Dependent Information                     2
   Figure 1-2:   Set Mode Characteristics Buffer                              3
   Figure 1-3:   IOSB Contents - Read and Write Function                      3
   Figure 1-4:   IOSB Contents - Set and Sense  Mode  and  Characteristics    3
                 Functions
$*$*EOD*$*$
$ checksum PSEUDO.DOC
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 1443436246
$ write sys$output "Creating PTY.PAS"
$ create PTY.PAS
$ DECK/DOLLARS="$*$*EOD*$*$"
(* Simple program to converse with the pseudo-terminal control device. *)

[inherit ('SYS$LIBRARY:STARLET')]
program PTY;

const
  buffer_size = 256;
  CTRL_BACKSLASH = chr (28);

type
  string = varying [256] of char;
  unsigned_byte = [byte] 0..255;
  unsigned_word = [word] 0..65535;
  characteristic_buffer = packed record
                            dev_class, dev_type : unsigned_byte;
                            width : unsigned_word;
                            charbits, extended_bits : unsigned;
                          end;
  status_block = packed record
                   status, count, terminator, terminator_size : unsigned_word;
                 end;

var
  stat : integer;
  mbx_chan, tt_chan, pty_chan : [static, volatile] unsigned_word;
  input_char : [static, volatile] char;
  buffer : packed array [1..buffer_size] of char;
  mbx_buffer : [static, volatile] packed array [1..40] of char;
  term_chars, old_term_chars : [static, volatile] characteristic_buffer;
  in_iosb, out_iosb, mbx_iosb : [static, volatile] status_block;
  exit_descriptor : [static] packed record
                      flink : unsigned;
                      handler : unsigned;
                      argnum : integer;
                      reason : ^integer;
                    end := (0, 0, 1, nil);
  message : [static, volatile] string :=
                                ''(13, 10, 7)'Got a mailbox message'(13, 10);

{-----------------------------------------------------------------------------}

  [external]
  function LIB$ASN_WTH_MBX
                (DEVNAM : [class_s] packed array [l1..u1:integer] of char;
                 MAXMSG, BUFQUO : integer;
                 var DEVCHAN, MBXCHAN : [volatile] unsigned_word
                ) : integer; extern;

(*
 * CLEANUP is our exit handler.  It resets the terminal characteristics.
 *)

  [unbound]
  function Cleanup (reason : integer) : integer;

  var
    stat : integer;
    iosb : status_block;

  begin (* Cleanup *)
    Cleanup := reason;
    stat := $QIOW (CHAN := tt_chan,
                     FUNC := IO$_SETMODE,
                     P1 := old_term_chars,
                     P2 := size (old_term_chars));
    if not odd (stat) then Cleanup := stat;
  end; (* Cleanup *)

  [asynchronous, unbound]
  procedure Set_asynch_mbx_input; forward;

(*
 * MBX_INPUT_AST is the AST handler invoked whenever we get something in
 * the associated mailbox.
 *)
  [asynchronous, unbound]
  procedure Mbx_input_AST;

  var
    stat : integer;
    iosb : status_block;

  begin (* Mbx_input_AST *)
    if not odd (mbx_iosb.status) then $EXIT (mbx_iosb.status);

    stat := $QIOW (CHAN := tt_chan,
                   FUNC := IO$_WRITEVBLK,
                   IOSB := iosb,
                   P1 := message.body,
                   P2 := message.length);
    if not odd (stat) then $EXIT (stat);
    if not odd (iosb.status) then $EXIT (iosb.status);

    Set_asynch_mbx_input;
  end; (* mbx_input_AST *)

(*
 * SET_ASYNCH_MBX_INPUT issues an asynchronous read on the terminal.
 *)
  procedure Set_asynch_mbx_input;

  var
    stat : integer;

  begin (* Set_asynch_mbx_input *)
    stat := $QIO (CHAN := mbx_chan,
                  FUNC := IO$_READVBLK,
                  IOSB := mbx_iosb,
                  ASTADR := mbx_input_AST,
                  P1 := mbx_buffer,
                  P2 := size (mbx_buffer));
    if not odd (stat) then $EXIT (stat);
  end; (* Set_asynch_mbx_input *)

  [asynchronous, unbound]
  procedure Set_asynch_input; forward;

(*
 * INPUT_AST is the AST handler invoked whenever we get something in
 * from the terminal.  Just ship it to the PTY.
 *)
  [asynchronous, unbound]
  procedure Input_AST;

  var
    stat : integer;

  begin (* Input_AST *)
    if not odd (in_iosb.status) then $EXIT (in_iosb.status);
    if input_char = CTRL_BACKSLASH then $EXIT (SS$_NORMAL);

    stat := $QIOW (CHAN := pty_chan,
                   FUNC := IO$_WRITEVBLK,
                   IOSB := in_iosb,
                   P1 := input_char,
                   P2 := 1);
    if not odd (stat) then $EXIT (stat);
    if not odd (in_iosb.status) then $EXIT (in_iosb.status);

    Set_asynch_input;
  end; (* Input_AST *)

(*
 * SET_ASYNCH_INPUT issues an asynchronous read on the terminal.
 *)
  procedure Set_asynch_input;

  var
    stat : integer;

  begin (* Set_asynch_input *)
    stat := $QIO (CHAN := tt_chan,
                  FUNC := IO$_READVBLK,
                  IOSB := in_iosb,
                  ASTADR := Input_AST,
                  P1 := input_char,
                  P2 := 1);
    if not odd (stat) then $EXIT (stat);
  end; (* Set_asynch_input *)

begin (* PTY *)
  stat := LIB$ASN_WTH_MBX (DEVNAM := 'PYA0:',
                            MAXMSG := size (mbx_buffer),
                            BUFQUO := 2 * size (mbx_buffer),
                            DEVCHAN := pty_chan,
                            MBXCHAN := mbx_chan);
  if not odd (stat) then $EXIT (stat);

  Set_asynch_mbx_input;

  stat := $ASSIGN (CHAN := tt_chan, DEVNAM := 'TT');
  if not odd (stat) then $EXIT (stat);

(* Get terminal characteristics. *)

  stat := $QIOW (CHAN := tt_chan,
                 FUNC := IO$_SENSEMODE,
                 P1 := term_chars,
                 P2 := size (term_chars));
  if not odd (stat) then $EXIT (stat);

  if term_chars.dev_class <> DC$_TERM then
    $EXIT (SS$_IVDEVNAM);

  old_term_chars := term_chars;

(* declare exit handler so that terminal chars are restored *)

  exit_descriptor.handler := iaddress (Cleanup);
  new (exit_descriptor.reason);

  stat := $DCLEXH (DESBLK := exit_descriptor);
  if not odd (stat) then $EXIT (stat);

(*
 * Set terminal characteristics we need:
 *    PASSTHRU, NOECHO, NOTTSYNC, NOHOSTSYNC
 *)

  with term_chars do
  begin
    extended_bits := UOR (extended_bits, TT2$M_PASTHRU);
    charbits := UOR (charbits, TT$M_NOECHO);
    charbits := UAND (charbits, UNOT (TT$M_TTSYNC));
    charbits := UAND (charbits, UNOT (TT$M_HOSTSYNC));
  end;

  stat := $QIOW (CHAN := tt_chan,
                 FUNC := IO$_SETMODE,
                 P1 := term_chars,
                 P2 := size (term_chars));
  if not odd (stat) then $EXIT (stat);

  Set_asynch_input;

  repeat
    stat := $QIOW (CHAN := pty_chan,
                   FUNC := IO$_READVBLK,
                   IOSB := out_iosb,
                   P1 := buffer,
                   P2 := size (buffer));
    if not odd (stat) then $EXIT (stat);
    if not odd (out_iosb.status) then $EXIT (out_iosb.status);

    stat := $QIOW (CHAN := tt_chan,
                   FUNC := IO$_WRITEVBLK,
                   IOSB := out_iosb,
                   P1 := buffer,
                   P2 := out_iosb.count);
    if not odd (stat) then $EXIT (stat);
    if not odd (out_iosb.status) then $EXIT (out_iosb.status);
  until false;
end. (* PTY *)
$*$*EOD*$*$
$ checksum PTY.PAS
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ exit

KVC@ENGVAX.SCG.HAC.COM (Kevin Carosso) (07/17/87)

$ show default
$ check_sum = 1276286148
$ write sys$output "Creating TPDRIVER.MAR"
$ create TPDRIVER.MAR
$ DECK/DOLLARS="$*$*EOD*$*$"
        .TITLE  TPDRIVER - Pseudo terminal driver for Front End and ethernet
        .IDENT  'V04-007'

;
;++
; FACILITY:
;
;       VAX/VMS TERMINAL DRIVER
;
; ABSTRACT:
;
;       PSEUDO TERMINAL ROUTINES.
;
; AUTHOR:
;
;       19-NOV-1982     Dale Moore (dwm) at CMU
;               Redone for VMS V3.0
;
;       This program has been granted to the public domain by the author.
;
; MODIFICATIONS:
;       Version 'V03-001':
;               DWM     - Added .Page above each .sbttl
;                       - added modem transition routines in disco and init
;                       - removed $ACBDEF external def's
;                       - restored timeout to resume
;                       - added ioc$initiate call in xoff and xon routines.
;       Version V03-002 - Changed TP startio to clear TIM in UCB$W_STS
;       Version V03-003 - Changed TP$RESUME to not set timeout bit in sts
;
;       Version V03-004 (Thu Dec  9 12:43:17 1982) D. Kashtan
;                         Made into a TEMPLATE driver.
;                         (***WARNING*** -- LOOK AT COMMENTS FOR ROUTINE
;                          TP$SET_LINE.  THERE IS IMPORTANT INFORMATION
;                          ABOUT CHANGES TO VMS THAT WILL AFFECT THE TPDRIVER)
;       Version V03-005 - (14-Jun-1983 )Dale Moore
;                         Add R4 to masks on calls to ioc$initiate.
;                         TTY$STARTIO clobber R4.
;       Version V03-006 - Change TP$XON and TP$XOFF routines from
;                         unconditionally calling IOC$INITIATE.
;                         IOC$INITIATE will branch to PY$STARTIO,
;                         Which will call TTY$GETNEXTCHAR (UCB$L_TT_GETNEXT),
;                         Which will branch to EOLSEEN,
;                         which calls TTY$READONE,
;                         which calls the PORT_XOFF routine which is TP$XOFF.
;                         This cycle was eating up all of the kernel stack.
;       Version V03-007 - (12-Jul-1983) Mark London
;                         Set default of TP to TERM to allow REPLY/USERS.
;                         Make TP owned and not a TEMPLATE in order that
;                         SHO TERM TPA0 does not cause a crash (SHOW attempts
;                         an ASSIGN to the device.)
;       Verison V04-001 - ( 9-Nov-1984 ) L. Bellamy and D. Davis
;                         In order of appearance:
;
;                         Added Object Rights Block offset definitions.
;                         Add TEMPLATE bit to UCB$W_STS.
;                         Add ORB definitions in prologue.
;                         Modify to allow vector definitions using VEC macro.
;                         Use CLASS_CTRL_INIT and CLASS_UNIT_INIT to locate
;                          vector table.
;                         Update powerfail routines.
;                         Use new methodology for XON/XOFF flow control.
;                         Get rid of all the SET_LINE stack manipulation
;                         since the terminal driver does not do anything
;                         malicious as suggested.
;
;                         NOTE - Comments in subroutine preambles not altered
;                         to reflect changes in most places.
;
;       Version V04-003 (24-Jun-1985) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Cleaned this thing up quite a bit.
;                       - Got rid of MBX characteristic on the devices.  This
;                         was a holdover to before cloned devices really
;                         existed.
;                       - Leave the TP template device OFFLINE.  This is what
;                         other TEMPLATE devices do, to indicate that you
;                         really cannot do I/O to the template.  Also left
;                         it UNAVAILABLE, since the only way it is supposed to
;                         get used is by cloning in the PY device.
;                       - Got rid of all modem operations.  Improper use tended
;                         to crash the system and they are not necessary.  TP
;                         device is always NOMODEM.  HANGUP works as you want
;                         it to without the modem stuff.
;                       - Setup forced characteristics and default permanent
;                         characteristics.  Forced are: NOAUTOBAUD, and NODMA.
;                         Default is HANGUP.
;                       - Fixed up TP$DISCONNECT to properly send a message
;                         to PY device's associated mailbox only if we are
;                         actually doing a hangup.  It used to do it every
;                         time.  Changed the message to MSG$_TRMHANGUP.
;                       - Got rid of the BRDCST on/off stuff.  It doesn't seem
;                         to be necessary any more.  It also had a bug in it
;                         somewhere that caused the terminal to start off
;                         NOBRDCST when it shouldn't.
;                       - General house-cleaning.  Got rid of commented out
;                         lines from VMS V3 version.  Fixed up typos in
;                         comments.
;
;       Version V04-004 (10-Feb-1986) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Changed all references to PTDRIVER to TPDRIVER because
;                       DEC (bless their little hearts) invented the %*%#$%
;                       TU81 and use PTA0: now.
;
;       Version V04-005 (3-Sep-1986) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Fixed bug whereby the sequence ^S followed by ^Y
;                       would cause a system hang.  Apparently the class
;                       driver calls the port RESUME routine when doing
;                       an ABORT and again when canceling the ^S.  Calling
;                       RESUME twice like this is bad.  In general, the
;                       port RESUME routine should not restart the output.
;                       In this driver, the RESUME and STOP routines aren't
;                       needed, so I got rid of them completely.
;
;                       Now on ^Y the ^S state is not cleared.  You still
;                       need to type ^Q to get your output (including the
;                       *INTERRUPT* echo string.  I thought this was another
;                       bug, but it's how my DHU-11 based terminals act
;                       and makes sense, because only a ^Q should really
;                       cancel a ^S.
;
;                       Also removed the TEMPLATE bit from the TPA0: template
;                       device since it was allowing people to $ASSIGN channels
;                       and get unhappy (lonely) TP devices that could crash
;                       the system.  Now, the only way to clone TP devices
;                       is through the code in PYDRIVER, hence by assigning
;                       a channel to PYA0:.  You can still $ASSIGN to TPA0:,
;                       but since the device is offline that shouldn't hurt
;                       anything.  This fixed crashes caused by KERMIT and
;                       other things attempting to use TPA0:.  You now get
;                       an error "DEVOFFLINE".
;
;                       Fixed a bug that caused a system crash from SHOW
;                       DEVICE/FULL of TPA0:.  The ACL_QUEUE bit was set
;                       in the ORB but the ACL queue was invalid (was zeroed).
;                       I got rid of the bit, now all is well.
;
;       Version V04-006 (5-Dec-1986) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Fixed the infamous character munging bug.  The fix
;                       is really in PYDRIVER.
;
;       Version V04-007 (10-JUL-1987) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       In TP$INITLINE make sure to tell the class driver
;                       never to time out.  This fix from Forest Kenney
;                       at DEC.
;
;                       Also, while we're in here, lets make the device
;                       acquire "NODE$" prefixes, since all the other
;                       terminal drivers do.
;--









        .PAGE
        .SBTTL  Declarations

        .LIBRARY        /SYS$LIBRARY:LIB.MLB/

;
; EXTERNAL DEFINITIONS:
;
.NOCROSS
        $CRBDEF                         ; DEFINE CRB
        $DCDEF                          ; DEVICE CLASSES AND TYPES
        $DDBDEF                         ; DEFINE DDB
        $DDTDEF                         ; DEFINE DDT
        $DEVDEF                         ; DEVICE CHARACTERISTICS
        $DYNDEF                         ; Dynamic structure definitions
        $IDBDEF                         ; DEFINE IDB OFFSETS
        $IODEF                          ; I/O Function Codes
        $IRPDEF                         ; IRP definitions
        $MSGDEF                         ; Message types
        $ORBDEF                         ; Define Object's Rights Block offsets
        $TTYDEF                         ; DEFINE TERMINAL DRIVER SYMBOLS
        $TTDEF                          ; DEFINE TERMINAL TYPES
        $TT2DEF                         ; Define Extended Characteristics
        $UCBDEF                         ; DEFINE UCB
        $VECDEF                         ; DEFINE VECTOR FOR CRB
        $TTYMACS                        ; DEFINE TERMINAL DRIVER MACROS
        $TTYDEFS                        ; DEFINE TERMINAL DRIVER SYMBOLS
.CROSS

;
; LOCAL DEFINITIONS
;
DT$_TP = ^XFF
;
; Definitions that follow the standard UCB fields for TP driver
;  This will all probably have to be the same as the standard term

        $DEFINI UCB                     ; Start of UCB definitions

        .=UCB$K_TT_LENGTH               ; Position at end of UCB

$DEF    UCB$L_TP_XUCB   .BLKL   1       ; UCB of corresponding
                                        ;  control/application unit
$DEF    UCB$K_TP_LEN                    ; Size of UCB

        $DEFEND UCB                     ; End of UCB definitions
;
; Definitions that follow the standard UCB fields in PY devices
;

        $DEFINI UCB                     ; Start of UCB definitions

        .=UCB$K_LENGTH          ; position at end of UCB

$DEF    UCB$L_PY_XUCB   .BLKL 1 ; UCB of terminal part of pseudo terminal

$DEF    UCB$K_PY_LEN            ; Size of UCB

        $DEFEND UCB             ; end of UCB definitions

;
; LOCAL STORAGE
;
        .PSECT  $$$105_PROLOGUE









        .PAGE
        .SBTTL  Standard Tables

;
; Driver prologue table:
;
TP$DPT::
        DPTAB   -                       ; DRIVER PROLOGUE TABLE
                END=TP_END,-            ; End and offset to INIT's vectors
                UCBSIZE=UCB$K_TP_LEN,-  ; SIZE OF UCB
                FLAGS=DPT$M_NOUNLOAD,-  ; Do not allow unload
                ADAPTER=NULL,-          ; ADAPTER TYPE
                NAME=TPDRIVER,-         ; NAME OF DRIVER
                VECTOR=PORT_VECTOR
        DPT_STORE INIT
        DPT_STORE UCB,UCB$W_UNIT_SEED,W,0       ; SET UNIT # SEED TO ZERO
        DPT_STORE UCB,UCB$B_FIPL,B,8    ; FORK IPL
        DPT_STORE UCB,UCB$L_DEVCHAR,L,<-; CHARACTERISTICS
                        DEV$M_REC!-     ;
                        DEV$M_IDV!-     ;
                        DEV$M_ODV!-     ;
                        DEV$M_TRM!-     ;
                        DEV$M_CCL>
        DPT_STORE UCB,UCB$L_DEVCHAR2,L, -               ; Device characteristics
                        <DEV$M_NNM>                     ; prefix with "NODE$"
        DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_TERM         ;
        DPT_STORE UCB,UCB$B_TT_DETYPE,B,TT$_UNKNOWN     ; TYPE
        DPT_STORE UCB,UCB$W_TT_DESIZE,@W,TTY$GW_DEFBUF  ; BUFFER SIZE
        DPT_STORE UCB,UCB$L_TT_DECHAR,@L,TTY$GL_DEFCHAR ; DEFAULT CHARACTERS
        DPT_STORE UCB,UCB$L_TT_DECHA1,@L,TTY$GL_DEFCHAR2; DEFAULT CHARACTERS
        DPT_STORE UCB,UCB$W_TT_DESPEE,@B,TTY$GB_DEFSPEED; DEFAULT SPEED
        DPT_STORE UCB,UCB$W_TT_DESPEE+1,@B,TTY$GB_RSPEED; DEFAULT SPEED
        DPT_STORE UCB,UCB$B_TT_DEPARI,@B,TTY$GB_PARITY  ; DEFAULT PARITY
        DPT_STORE UCB,UCB$B_TT_PARITY,@B,TTY$GB_PARITY  ; DEFAULT PARITY
        DPT_STORE UCB,UCB$B_DEVTYPE,B,TT$_UNKNOWN       ; TYPE
        DPT_STORE UCB,UCB$W_DEVBUFSIZ,@W,TTY$GW_DEFBUF  ; BUFFER SIZE
        DPT_STORE UCB,UCB$L_DEVDEPEND,@L,TTY$GL_DEFCHAR ; DEFAULT CHARACTERS
        DPT_STORE UCB,UCB$L_TT_DEVDP1,@L,TTY$GL_DEFCHAR2; Default Characters
        DPT_STORE UCB,UCB$W_TT_SPEED,@B,TTY$GB_DEFSPEED ; DEFAULT SPEED
        DPT_STORE UCB,UCB$W_TT_SPEED+1,@B,TTY$GB_RSPEED ; DEFAULT SPEED
        DPT_STORE UCB,UCB$B_DIPL,B,8                    ; DEV IPL (no device)
        DPT_STORE UCB,UCB$L_TT_WFLINK,L,0       ; Zero write queue.
        DPT_STORE UCB,UCB$L_TT_WBLINK,L,0       ; Zero write queue.
        DPT_STORE UCB,UCB$L_TT_RTIMOU,L,0       ; Zero read timed out disp.
;
; Added ORB definitions
;
        DPT_STORE ORB, ORB$B_FLAGS, B, <ORB$M_PROT_16>
        DPT_STORE ORB, ORB$W_PROT, @W, TTY$GW_PROT
        DPT_STORE ORB, ORB$L_OWNER, @L, TTY$GL_OWNUIC

        DPT_STORE DDB,DDB$L_DDT,D,TP$DDT

        DPT_STORE REINIT
        DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,D,TP$INITIAL; CONTROLLER INIT
        DPT_STORE CRB,CRB$L_INTD+VEC$L_UNITINIT,D,TP$INITLINE; UNIT INIT
        DPT_STORE END

        DDTAB   DEVNAM  = TP,-          ; Dummy TP port Driver Dispatch table
                START   = 0,-
                FUNCTB  = 0

        .PSECT $$$115_DRIVER,LONG

; The associated class driver uses this table to command the port driver.
; The address of the table is contained in the terminal UCB extension area.
; The offset definitions are defined by the ttydefs.

;
; TP specific dispatch table
;
PORT_VECTOR:
;
; Added port vector table using VEC macros
;
        $VECINI TP,TP$NULL
        $VEC    STARTIO,TP$STARTIO
        $VEC    DISCONNECT,TP$DISCONNECT
        $VEC    SET_LINE,TP$SET_LINE
        $VEC    XON,TP$XON
        $VEC    XOFF,TP$XOFF
        $VEC    ABORT,TP$ABORT
        $VECEND

TP$NULL:                                ; Null port routine
        RSB










        .PAGE
        .SBTTL  TP$INITIAL - Initialize pseudo terminal interface
;++
; TP$INITIAL - INITIALIZE INTERFACE
;
; FUNCTIONAL DESCRIPTION:
;
; This routine is entered at device CONNECT time and power recovery.
; All we do is connect ourselves up to the class driver.
;
; INPUTS:
;
;       R4 = ADDRESS OF THE UNIT CSR
;       R5 = IDB OF UNIT
;       R8 = ADDRESS OF THE UNIT CRB
;
; OUTPUTS:
;
;       R0, R1, R2, R3 are destroyed.
;
; IMPLICIT INPUTS:
;
;       IPL = IPL$_POWER
;
;--
TP$INITIAL::

        CLASS_CTRL_INIT TP$DPT,PORT_VECTOR
        MOVB    #DT$_TP,CRB$B_TT_TYPE(R8)
        RSB









        .PAGE
        .SBTTL  TP$INITLINE - RESET INDIVIDUAL LINE
;++
; TP$INITLINE - RESET pseudo terminal control state
;
; FUNCTIONAL DESCRIPTION:
;
; This routine performs a simple unit initialization.
;
;
; INPUTS:
;
;       R5 = UCB ADDRESS
;
; OUTPUTS:
;
;       R2,R5 ARE PRESERVED.
;--
TP$INITLINE::                           ; RESET SINGLE LINE
        MOVAL   TP$VEC,R0               ; Set TP port vector table
        CLASS_UNIT_INIT
        TSTL    UCB$W_UNIT(R5)          ; Skip initialization of TEMPLATE
        BEQL    40$                     ; Unit #0 = Template: Skip everything!

        BBS     #UCB$V_POWER,UCB$W_STS(R5),-    ; Skip if powerfail recovery
                20$
        movl    UCB$L_TT_LOGUCB(r5), r1         ; Look at logical term UCB
        beql    10$                             ; If none, then has no refs
        tstw    UCB$W_REFC(r1)                  ; See if TP has any references
        BNEQ    20$                             ; If so don't reinit ucb
10$:    bsb     set_forced_chars                ; Set required characteristics
        BISL    #TT2$M_HANGUP,-                 ; Set default characteristics
                UCB$L_TT_DECHA1(R5)
        MOVL    UCB$L_TT_CLASS(R5),R1           ; Address class vector table
        JSB     @CLASS_SETUP_UCB(R1)            ; Init ucb fields
        bisw    #TTY$M_PC_NOTIME, -
                UCB$W_TT_PRTCTL(r5)             ; Class driver not to time out

20$:    BBC     #UCB$V_POWER,UCB$W_STS(R5),40$  ; Powerfail handler
        MOVL    UCB$L_TT_CLASS(R5),R0
        JMP     @CLASS_POWERFAIL(R0)

40$:    RSB

;
; This little routine sets certain required characteristics.  It is called by
; the INITLINE code to set them at the outset and by the SETLINE code to reset
; them unconditionally if someone tries to set them.
;
set_forced_chars:
        bicl2   #<TT2$M_DMA ! TT2$M_AUTOBAUD>, -
                UCB$L_TT_DEVDP1(R5)
        bicl2   #<TT2$M_DMA ! TT2$M_AUTOBAUD>, -
                UCB$L_TT_DECHA1(R5)
        rsb










;++
; TP$SET_LINE - Used to Reset SPEED and UCB
;
; FUNCTIONAL DESCRIPTION:
;
;       Called whenever someone tries to set terminal modes/characteristics.
;       All we do is reset anything that we think should never be changed.
;
; INPUTS:
;
;       R5 = UCB ADDRESS of TP
;
; OUTPUTS:
;
;       none
;--
TP$SET_LINE::
        brb     set_forced_chars











        .Page
;++
; TP$DISCONNECT - SHUT OFF UNIT
;
; FUNCTIONAL DESCRIPTION:
;
; This routine is used when for some reason the unit must be disconnected.
; This can be at hangup or last deassign.  If the PY device has an associated
; mailbox, signal an MSG$_TRMHANGUP in it.
;
; Although we are disconnecting a virtual device, we don't do anything
; more than send a hangup message because we want to allow the device to
; be reusable.  It's really only if the control device (PY) goes away
; that we mark the TP offline and delete it's UCB.  That code's all in
; PYDRIVER.
;
; INPUTS:
;
;       R0 = (0 for hangup, 1 for nohangup)
;       R5 = UCB ADDRESS of TP
;
; OUTPUTS:
;
;       R3,R4 ARE USED.
;--
TP$DISCONNECT::
        blbs    r0, 99$                 ; If no hangup, skip all this.

        PUSHR   #^M<R0,R1,R2,R3,R4,R5>  ; Save the registers

        MOVL    UCB$L_TP_XUCB(R5),R5    ; Get PY UCB
        BEQL    10$                     ; If disconnected, ignore
        MOVL    UCB$L_AMB(R5),R3        ; Load Associated Mailbox of PY UCB
        BEQL    10$                     ; If EQL then no mailbox
        MOVZWL  #MSG$_TRMHANGUP,R4      ; Load Message Type
        JSB     G^EXE$SNDEVMSG          ; Send the message
10$:    POPR    #^M<R0,R1,R2,R3,R4,R5>  ; Restore everything
99$:    RSB











        .PAGE
        .SBTTL  TP START I/O ROUTINE
;++
; TP$STARTIO - START I/O OPERATION ON TP
;
; FUNCTIONAL DESCRIPTION:
;
;       This routine is entered from the device independent terminal startio
;       routine to enable output interrupts on an idle TP unit
;
; INPUTS:
;
;       R3 =    Character       AND     CC = Plus (N EQL 0)
;       R3 =    Address         AND     CC = Negative (N EQL 1)
;
;       R5 = UCB ADDRESS
;
; OUTPUTS:
;
;       R5 = UCB ADDRESS
;--
TP$STARTIO::                            ; START I/O ON UNIT
        BGEQ    20$                     ; Single character
        BISW    #TTY$M_TANK_BURST,-     ; Signal burst active
                UCB$W_TT_HOLD(R5)
10$:
;
; Here we must do something to notify our mate device that
; there is data to pick up
;
        PUSHR   #^M<R0,R2,R3,R4,R5>     ; Save TP UCB
        MOVL    UCB$L_TP_XUCB(R5),R5    ; Switch to PY UCB
        BEQL    17$                     ; PY is disconnected: skip
        DSBINT  UCB$B_FIPL(R5)
        BBC     #UCB$V_BSY,-            ; If the device isn't busy,
                UCB$W_STS(R5),15$       ; then dont do i/o
        MOVL    UCB$L_IRP(R5),R3        ; Get IRP address
        JSB     G^IOC$INITIATE          ; IOC$INITIATE needs IRP addr
15$:    ENBINT
16$:    POPR    #^M<R0,R2,R3,R4,R5>     ; Switch back to TP UCB
        RSB
;
; Come here if we have no PY control device to send stuff to.  Just
; suck all the data we can out of the class driver and throw it away.
;
17$:    POPR    #^M<R0,R2,R3,R4,R5>     ; Switch back to TP UCB
18$:    bicb    #UCB$M_INT, UCB$W_STS(r5)
        jsb     @UCB$L_TT_GETNXT(r5)
        tstb    UCB$B_TT_OUTYPE(r5)
        bneq    18$
        rsb

20$:
        MOVB    R3,UCB$W_TT_HOLD(R5)    ; Save output character
        BISW    #TTY$M_TANK_HOLD,-      ; Signal charater in tank
                UCB$W_TT_HOLD(R5)
        BRB     10$









        .PAGE
        .SBTTL  Port Routines Stop,Resume,XON,XOFF
;++
; TP$XOFF -     Send Xoff
; TP$XON -      Send Xon
; TP$ABORT -    Abort current output
;
; Functional Description:
;
;       These routines are used by the terminal class driver to
;       control output on the port
;
; Inputs:
;
;       R5 = UCB Address
;
; Outputs:
;
;       R5 = UCB Address
;--
        .ENABLE LSB
;
; Schedule xon/xoff to be sent
;
TP$XOFF:
TP$XON:
;
; Changed schedule bit mask
;
        BISW    #TTY$M_TANK_PREMPT,UCB$W_TT_HOLD(R5)    ; Schedule xon
        MOVB    R3,UCB$B_TT_PREMPT(R5)                  ; Save character
20$:
        RSB
        .DISABLE LSB
;
; Abort any port currently active
;
TP$ABORT:
5$:     BBCC    #TTY$V_TANK_BURST,UCB$W_TT_HOLD(R5),-   ; reset burst active
                10$
10$:    RSB

TP_END:                         ; End of driver

        .END
$*$*EOD*$*$
$ checksum TPDRIVER.MAR
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ exit

KVC@ENGVAX.SCG.HAC.COM (Kevin Carosso) (07/17/87)

$ show default
$ check_sum = 1596636955
$ write sys$output "Creating PYDRIVER.MAR"
$ create PYDRIVER.MAR
$ DECK/DOLLARS="$*$*EOD*$*$"
        .TITLE  PYDRIVER - Pseudo terminal driver interface
        .IDENT 'V04-007'

;
;++
; FACILITY:
;
;       VAX/VMS Pseudo Terminal Driver interface
;
; ABSTRACT:
;
;       The pseudo terminal consists of two devices.
;       This is the non terminal part of the two devices.
;
; AUTHOR:
;
;       19-Nov-1982     Dale Moore      Redid the TP driver for VMS 3.0
;
;       This program has been granted to the public domain by the author.
;
; Revision History:
;
;       Version 'V03-001'
;               DWM     - Added Page seperators
;                       - On Last cancel, invoke hangup on TP device
;                       - changed PY_STOP and PY_STOP2 to return instead
;                         of looping for more.
;                       - Changed last cancel to call ioc$reqcom instead of
;                         using macro REQCOM which is a branch ioc$reqcom.
;       Version V03-002 - Changed to Clear word rather than clear byte
;                         in startio routine on word field.
;
;       Version V03-003 (Thu Dec  9 12:42:38 1982) D. Kashtan
;                         Made into a TEMPLATE driver.
;       Version V03-004 (Fri Dec 10 11:40:35 1982) D. Kashtan
;                         Made EXE$... into +EXE$... in FDT dispatch table,
;                         fixing bug that crashed system in SET/SENSE MODE/CHAR
;       Version V03-005 (14-Jun-1983) Dale Moore
;                         Add R4 to calls to IOC$INITIATE.
;                         TTY$STARTIO mucks R4
;       Version V03-006 (12-Jul-1983) Mark London, MIT Plasma Fusion Center
;                       - Set terminal to NOBROADCAST when no READ QIO avail-
;                         able so as to allow Broadcasts without hanging up.
;                         (When no QIO available, UCB$M_INT is enabled, and
;                         the Broadcast don't get handled.  The sender of a
;                         Broadcast goes into a wait state until the broadcast
;                         is completed or timed-out, neither or which can
;                         happen.  Setting NOBROADCASTs at least allow the
;                         Broadcast to finish. What is needed is a CTRLS state
;                         that doesn't allow Broadcasts to break through.)
;                       - Added MOVC3 instruction for burst data in PY$STARTIO,
;                         which "should" speed up the transfers.
;                       - Fixed data transfer problem by raising to fork IPL
;                         while calling PUTNXT in PY$FDTWRITE. NOTE: TPA0 must
;                         be a mailbox to avoid TT reads from timing out.
;
;       Version V04-001 - Doug Davis, Digital Equipment
;                       - Most of the changes required for migration to
;                         Version 4.0 relate to the new handling of UCB
;                         creation and deletion. This includes adding
;                         a CLONEDUCB entry point to the dispatch table,
;                         and "cloning" the UNITINIT routine to handle the
;                         required entry. Also changed the call from
;                         IOC$CREATE_UCB to IOC$CLONE_UCB, with associated
;                         maintainence of the UCB$V_DELETEUCB bit in the
;                         UCB$L_STS field.
;                       - Changes were also incorporated reflecting new
;                         methods of  XON/XOFF flow control.
;                       - Although pieces of the original code have been
;                         superceded by these changes ( example - functions
;                         that were performed by Unit_Init for new units
;                         are are now performed by Clone_Init ), most of
;                         the original code was left in place and/or commented
;                         out.
;
;                       NOTE - No subroutines preambles were modified to
;                              reflect these changes.
;
;       Version V04-002 (20-Jan-1985) Mark London, MIT Plasma Fusion Center
;                       - Changed test for output characters after call to
;                         UCB$L_TT_PUTNXT and UCB$L_TT_GETNXT.  Output is
;                         indicated in UCB$B_TT_OUTYPE.
;
;       Version V04-003 (24-Jun-1985) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Cleaned this thing up quite a bit.
;                       - Got rid of MBX characteristic on the devices.  This
;                         was a holdover to before cloned devices really
;                         existed.
;                       - Got rid of the UNIT_INIT routine completely.  This
;                         was replaced by a CLONE_UCB routine.
;                       - Leave the PY template device OFFLINE.  This is what
;                         other TEMPLATE devices do, to indicate that you
;                         really cannot do I/O to the template.
;                       - Rewrote the CANCEL_IO routine to issue a DISCONNECT
;                         on the TP device at last deassign of the PY device.
;                         This causes the TP device to hangup on it's process.
;                         Works quite nicely with VMS V4 connect/disconnect
;                         mechanism.  Also, the devices should never stay
;                         around after last deassign on the PY, if you want to
;                         reconnect, count on VMS connect/disconnect instead,
;                         it's much less of a security hole.
;                       - Got rid of all modem operations.  Improper use tended
;                         to crash the system and they are not necessary.  TP
;                         device is always NOMODEM.  HANGUP works as you want
;                         it to without the modem stuff.
;                       - Got rid of the BRDCST on/off stuff.  It doesn't seem
;                         to be necessary any more.  It also had a bug in it
;                         somewhere that caused the terminal to start off
;                         NOBRDCST when it shouldn't.
;                       - General house-cleaning.  Got rid of commented out
;                         lines from VMS V3 version.  Fixed up typos in
;                         comments.
;
;       Version V04-004 (10-Feb-1986) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Changed all references to PTDRIVER to TPDRIVER because
;                       DEC (bless their little hearts) invented the *#&#$&
;                       TU81 and use PTA0: now.
;
;       Version V04-005 (3-Sep-1986) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Fixed bug whereby the sequence ^S followed by ^Y
;                       would cause a system hang.  The fix is really in
;                       TPDRIVER.
;
;       Version V04-006 (5-Dec-1986) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Fixed the infamous character munging bug.  Turns
;                       out that in FDTWRITE we were enabling interrupts
;                       after doing the PUTNXT and before checking for
;                       a char.  Now check for the character and then
;                       ENBINT after we've decided what to do.  I assume
;                       UCB$B_TT_OUTYPE field was getting corrupted.
;
;       Version V04-007 (10-JUL-1987) Kevin Carosso, Hughes Aircraft Co., S&CG
;                       Fix in TPDRIVER for timeouts.  Don't bother
;                       clearing the TIM bit all the time in here now.
;
;                       Also, while we're in here, lets make the device
;                       acquire "NODE$" prefixes, since mailboxes do.
;--









        .PAGE
        .SBTTL  Declarations

        .LIBRARY        /SYS$LIBRARY:LIB.MLB/

;
; External Definitions:
;

.NOCROSS
;       $ACBDEF                         ; Define ACB
        $CRBDEF                         ; Define CRB
        $CANDEF                         ; Define cancel codes
        $DDBDEF                         ; DEFINE DDB
        $DDTDEF                         ; DEFINE DDT
        $DEVDEF                         ; DEVICE CHARACTERISTICS
        $DYNDEF                         ; Dynamic structure definitions
        $IODEF                          ; I/O Function Codes
        $IRPDEF                         ; IRP definitions
        $JIBDEF                         ; Define JIB offsets
        $SSDEF                          ; DEFINE System Status
        $PCBDEF                         ; Define PCB
        $PRDEF                          ; Define PR
        $TTYDEF                         ; DEFINE TERMINAL DRIVER SYMBOLS
        $TTDEF                          ; DEFINE TERMINAL TYPES
        $TT2DEF                         ; Define Extended Characteristics
        $UCBDEF                         ; DEFINE UCB
        $VECDEF                         ; DEFINE VECTOR FOR CRB
        $TTYMACS                        ; DEFINE TERMINAL DRIVER MACROS
        $TTYDEFS                        ; DEFINE TERMINAL DRIVER SYMBOLS
.CROSS

;
; Local definitions
;
; QIO Argument list offsets
;
P1 = 0
P2 = 4
P3 = 8
P4 = 12
P5 = 16
P6 = 20
;
; New device class for control end
;
DC$_PY = ^XFF
DT$_PY = 0

;
; Definitions that follow the standard UCB fields for TP driver
;  This will all probably have to be the same as the standard term

        $DEFINI UCB                     ; Start of UCB definitions

        .=UCB$K_TT_LENGTH               ; Position at end of UCB

$DEF    UCB$L_TP_XUCB   .BLKL   1       ; UCB of corresponding
                                        ;  control/application unit
$DEF    UCB$K_TP_LEN                    ; Size of UCB

        $DEFEND UCB                     ; End of UCB definitions

;
; Definitions that follow the standard UCB fields in PY devices
;

        $DEFINI UCB                     ; Start of UCB definitions

        .=UCB$K_LENGTH          ; position at end of UCB

$DEF    UCB$L_PY_XUCB   .BLKL 1 ; UCB of terminal part of pseudo terminal

$DEF    UCB$K_PY_LEN            ; Size of UCB

        $DEFEND UCB             ; end of UCB definitions









        .PAGE
;
; LOCAL Storage
;
        .PSECT $$$105_PROLOGUE

        .SBTTL  Standard Tables

;
; Driver prologue table:
;
PY$DPT::
        DPTAB   -                       ; Driver Prologue table
                END = PY$END,-          ; End and offset to INIT's vectors
                UCBSIZE = UCB$K_PY_LEN,-; Size of UCB
                FLAGS=DPT$M_NOUNLOAD,-          ; Don't allow unload
                ADAPTER=NULL,-                  ; ADAPTER TYPE
                NAME    = PYDRIVER              ; Name of driver
        DPT_STORE INIT
        DPT_STORE UCB,UCB$W_UNIT_SEED,W,0       ; SET UNIT # SEED TO ZERO
        DPT_STORE UCB,UCB$B_FIPL,B,8            ; Fork IPL
        DPT_STORE UCB,UCB$W_STS,W,-             ; TEMPLATE device
                        <UCB$M_TEMPLATE>
        DPT_STORE UCB,UCB$L_DEVCHAR,L,<-        ; Characteristics
                        DEV$M_REC!-             ;   record oriented
                        DEV$M_AVL!-             ;   available
                        DEV$M_IDV!-             ;   input device
                        DEV$M_ODV>              ;   output device
        DPT_STORE UCB,UCB$L_DEVCHAR2,L, -               ; Device characteristics
                        <DEV$M_NNM>                     ; prefix with "NODE$"
        DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_PY
        DPT_STORE UCB,UCB$B_DIPL,B,8            ; Device IPL = FIPL (no device)
        DPT_STORE DDB,DDB$L_DDT,D,PY$DDT

        DPT_STORE REINIT
        DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,D,PY$INITIAL     ; Controller
        DPT_STORE END









        .PAGE
        .SBTTL Driver Dispatch table and function decision table
;
; Driver Dispatch table
;
        DDTAB   DEVNAM  = PY,-                  ; Device name
                START   = PY$STARTIO,-          ; Start I/O routine
                FUNCTB  = PY$FUNCTAB,-          ; The function table
                CANCEL  = PY$CANCEL,-           ; the cancel i/o routine
                CLONEDUCB = PY$CLONE_INIT       ; Entry when template cloned.
;
; Function Decision table for PY devices
;
PY$FUNCTAB:
        FUNCTAB ,-                      ; Legal Functions
                <READLBLK,-
                WRITELBLK,-
                READVBLK,-
                WRITEVBLK,-
                READPBLK,-
                WRITEPBLK,-
;               SETMODE,-               ; Disallow, can crash and not needed
;               SETCHAR,-               ; /kvc
                SENSEMODE,-
                SENSECHAR,-
                >
        FUNCTAB ,-                      ; Buffered I/O functions
                <READLBLK,-
                WRITELBLK,-
                READVBLK,-
                WRITEVBLK,-
                READPBLK,-
                WRITEPBLK,-
                >
        FUNCTAB PY$FDTREAD,<READLBLK,READVBLK,READPBLK>
        FUNCTAB PY$FDTWRITE,<WRITELBLK,WRITEVBLK,WRITEPBLK>
;       FUNCTAB +EXE$SETMODE,<SETMODE>
;       FUNCTAB +EXE$SETCHAR,<SETCHAR>
        FUNCTAB +EXE$SENSEMODE,<SENSEMODE,SENSECHAR>

        .SBTTL  Local Storage - Name of companion device

TPSTRING:       .ASCII  /TPA/
TPLENGTH = . - TPSTRING









        .PAGE
        .SBTTL  PY$FDTREAD - Function decision routine for PY control read
;++
; PY$FDTREAD
;
; Functional Description:
;
;       This routine is called from the function decision table dispatcher
;       to process a read physical, read logical, read virtual I/O function.
;
;       The function first verifies the caller's parameters, terminating
;       the request with immediate success or error if necessary.
;       A system buffer is allocated and its
;       address is saved in the IRP.  The caller's quota is updated, and
;       the read request is queued to the driver for startup.
;
; Inputs:
;
;       R0,R1,R2        = Scratch
;       R3              = IRP Address
;       R4              = Address of PCB for current process
;       R5              = Device UCB address
;       R6              = Address of CCB
;       R7              = I/O function code
;       R8              = FDT Dispatch addr
;       R9,R10,R11      = Scratch
;       AP              = Address of function parameter list
;                       P1(AP) = Buffer Address
;                       P2(AP) = Buffer Size
;
; Outputs:
;
;       R0,R1,R2,R11    = Destroyed
;       R3-R10,AP       = Preserved (pickled)
;       IRP$L_SVAPTE(R3)= Address of allocated system buffer
;       IRP$W_BOFF(R3)  = Requested byte count
;
;       System Buffer:
;               LONGWORD/0      = Address of start of data= buff+12
;               LONGWORD/1      = Address of user buffer
;
;--
PY$FDTREAD::
        MOVZWL  P2(AP),R1       ; Get buffer Size
        BNEQ    15$
        JMP     10$             ; Is the size zero? If so, go do it easy.
15$:    MOVL    P1(AP),R0       ; Get buffer Address
        JSB     G^EXE$READCHK   ; Do we have access to the buffer
        PUSHR   #^M<R0,R3>      ; Save user buffer address and IRP address
        ADDL    #12,R1          ; Add 12 bytes for buffer header
        JSB     G^EXE$BUFFRQUOTA; Is there enough buffer space left in
                                ;  the quota?
        BLBC    R0, 30$         ; Branch if insufficient quota
        JSB     G^EXE$ALLOCBUF  ; Allocate a system buffer
        BLBC    R0, 30$         ; Branch if none available
        POPR    #^M<R0,R3>      ; Restore user buffer and irp address
        MOVL    R2,IRP$L_SVAPTE(R3)     ; Save address of buffer
        MOVW    R1,IRP$W_BOFF(R3)       ;  and requested byte count
        MOVZWL  R1,R1                   ; convert to longword count
        MOVL    PCB$L_JIB(R4),R11       ; Get Jib address
        SUBL    R1,JIB$L_BYTCNT(R11)    ; Adjust quota count
        MOVAB   12(R2),(R2)             ; Save addr of start of user data
        MOVL    R0,4(R2)                ; Save user buffer address in 2nd
                                        ; longword
        JMP     G^EXE$QIODRVPKT ; Queue I/O packet to start I/O routine
;
; Did he request a read of zero bytes?
;
10$:    MOVL    #SS$_NORMAL,R0          ; Everything is ok
        JMP     G^EXE$FINISHIOC         ; complete I/O request
;
; Come here when something goes wrong
;
30$:    POPR    #^M<R0,R3>              ; Clear buffer addr and restore IRP
        JMP     G^EXE$ABORTIO           ; complete I/O request









        .PAGE
        .SBTTL  PY$FDTWRITE - Function decision routine for PY control write
;++
; PY$FDTWRITE
;
; Functional Description:
;
;       This routine is called from the function decision table dispatcher
;       to process a write physical, write logical, write virtual I/O
;       function.
;
;       The function first verifies the caller's parameters, terminating
;       the request with immediate success or error if necessary.
;       The routine then immediately start cramming the characters into
;       the associated units typeahead buffer by calling putnxtchr.
;
; Inputs:
;
;       R0,R1,R2        = Scratch
;       R3              = IRP Address
;       R4              = Address of PCB for current process
;       R5              = Device UCB address
;       R6              = Address of CCB
;       R7              = I/O function code
;       R8              = FDT Dispatch addr
;       R9,R10,R11      = Scratch
;       AP              = Address of function parameter list
;               P1(AP)  = Buffer Address
;               P2(AP)  = Buffer Size
;
; Outputs:
;
;       R0,R1,R2        = Destroyed
;       R3-R8,AP        = Preserved (pickled)
;
; External Routines:
;
;       EXE$ABORTIO - FDT abort io routine
;       Input Parameters:
;               R0 - First longword of IOSB
;               R3 - IRP Address
;               R4 - PCB Address
;               R5 - UCB Address
;
;       EXE$FINISHIOC - FDT finish IO routine
;       Input Parameters:
;               R0 - First longword of IOSB
;               R3 - IRP Address
;               R4 - PCB Address
;               R5 - UCB Address
;
;       EXE$WRITECHK - Check access to buffer
;       Input Parameters:
;               R0 - Address of buffer
;               R1 - Size of buffer
;               R3 - IRP Address
;       Output Parameters:
;               R0,R1,R3 - Preserved
;               R2 - clear
;
;       @UCB$L_TT_PUTNXT(R5) - Port driver input character routine
;       Input Parameters:
;               R3 - character
;               R5 - UCB Address
;       Output Parameters:
;               R3 - if EQL then nothing
;                    if LSS then Burst address to output
;                    if GTR then char to output
;               R5 - UCB Address
;               R1,R2,R4 - trashed
;               R0 - Is this trashed or preserved? Documentation say preserve.
;
;--
PY$FDTWRITE::
        MOVZWL  P2(AP),R1       ; Get buffer Size
        BEQL    40$             ; Is the size zero? If so, go do it easy.
        MOVL    P1(AP),R0       ; Get buffer Address
        JSB     G^EXE$WRITECHK  ; Do we have access to the buffer
                                ; No return means no access
;
; Change the UCB's around abit
;
        PUSHR   #^M<R3,R4,R5>
        MOVZWL  R1,R9                   ; Set size in R9
        MOVL    UCB$L_PY_XUCB(R5),R5    ; Set fake UCB in R5
;
; Loop through the packet R0 is address R9 is size
;
10$:
        MOVZBL  (R0)+,R3                ; Get the byte and set address to next
        DSBINT  UCB$B_FIPL(R5)
        JSB     @UCB$L_TT_PUTNXT(R5)    ; Buffer the character
        TSTB    UCB$B_TT_OUTYPE(R5)     ; Test output
        BLEQ    20$                     ; None or string output
        ENBINT
        MOVB    R3,UCB$W_TT_HOLD(R5)    ; save the character in tank
        BISW    #TTY$M_TANK_HOLD,-
                UCB$W_TT_HOLD(R5)       ; Signal char in tank
        BRB     25$
20$:
        BEQL    30$                     ; No character
        ENBINT
        BISW    #TTY$M_TANK_BURST,-     ; Signal burst
                UCB$W_TT_HOLD(R5)
25$:    PUSHR   #^M<R0,R2,R3,R5,R9>     ; Save State
        MOVL    UCB$L_TP_XUCB(R5),R5    ; Switch to PY UCB
        DSBINT  UCB$B_FIPL(R5)
        BBC     #UCB$V_BSY,UCB$W_STS(R5),27$
        MOVL    UCB$L_IRP(R5),R3        ; Get IRP
        JSB     G^IOC$INITIATE          ; Go to the start I/O
27$:    ENBINT
        POPR    #^M<R0,R2,R3,R5,R9>     ; Restore State
        BRB     35$

30$:    ENBINT
35$:    DECW    R9                      ; decrease number to do
        BGTR    10$                     ; if gtr then more to do
        POPR    #^M<R3,R4,R5>           ; Restore real UCB and IRP
;
; Finish up the read
;
40$:
        MOVZWL  P2(AP),R0               ; Move number of bytes output
        ASHL    #16,R0,R0               ; Put it in the high word
        MOVW    #SS$_NORMAL,R0          ; Everything is just fine
        JMP     G^EXE$FINISHIOC         ; Complete the I/O request









        .PAGE
        .SBTTL  PY$CANCEL - Cancel the IO on the PY device
;++
;
; Functional Description:
;
;       This routine is entered to stop io on a PY unit.  If this is the last
;       deassign on the PY device, issue a CLASS_DISCONNECT on our associated
;       TP device to get it away from any processes using it.
;
; Inputs:
;
;       R2 = Negative of the Channel Number,
;               also called channel index number
;       R3 = Current IO package address
;       R4 = PCB of canceling process
;       R5 = UCB Address
;       R8 = CAN$C_CANCEL on CANCEL IO or CAN$C_DASSGN on DEASSIGN
;
; Outputs:
;       Everything should be preserved
;--
PY$CANCEL::                                     ; Cancel PY usage
        JSB     G^IOC$CANCELIO                  ; Call the cancel routine
        BBC     #UCB$V_CANCEL,UCB$W_STS(R5),10$ ; Branch if not for this guy
        MOVQ    #SS$_ABORT,R0                   ; Status is request canceled
        BICW    #<UCB$M_BSY!UCB$M_CANCEL>,-     ;
                UCB$W_STS(R5)                   ; Clear unit status flags
        JSB     G^IOC$REQCOM                    ; Complete request
10$:    TSTW    UCB$W_REFC(R5)                  ; Last Deassign
        BNEQ    100$                            ; No, just exit
;
; Do a DISCONNECT on the TP device.
;
        pushr   #^M<r2, r3, r4, r5>
        movl    UCB$L_PY_XUCB(r5), r5           ; Switch to TP UCB
        beql    20$                             ; if not there, skip
        clrl    UCB$L_TP_XUCB(r5)               ; Clear backlink to PY device
        bisl2   #UCB$M_DELETEUCB, UCB$L_STS(r5) ; Set it to go bye-bye
        bicw2   #UCB$M_ONLINE,UCB$W_STS(R5)     ; Mark offline
        bicb2   #UCB$M_INT, UCB$W_STS(r5)       ; Don't expect interrupt
        movl    UCB$L_TT_LOGUCB(r5), r1         ; Look at logical term UCB
        tstw    UCB$W_REFC(r1)                  ; See if TP has any references
        bneq    15$                             ; If so, go and do disconnect
        jsb     G^IOC$DELETE_UCB                ; if not, delete the UCB
        brb     20$
15$:    clrl    r0                              ; indicate that we must hangup
        movl    UCB$L_TT_CLASS(r5), r1
        jsb     @CLASS_DISCONNECT(r1)           ; Force disconnect
20$:    popr    #^M<r2, r3, r4, r5>             ; Switch back to PY UCB
        clrl    UCB$L_PY_XUCB(r5)               ; Clear link to deleted TP
        bisl2   #UCB$M_DELETEUCB, UCB$L_STS(r5) ; Set our own delete bit
100$:   rsb









        .PAGE
        .SBTTL PY$INITIAL - Initialize Pseudo terminal interface

;++
; PY$INITIAL - Initialize the interface
;
; Functional Description:
;
;       This routine is entered at device connect time and power recovery.
;       There isn't much to do to the device.
;
; Inputs:
;
;       R4 = The devices CSR  (but there is no csr!)
;       R5 = address of IDB
;       R6 = address of DDB
;       R7 = address of CRB
;
; Outputs:
;
;       All registers preserved
;
;--
PY$INITIAL::
        RSB









        .PAGE
        .SBTTL  PY$CLONE_INIT - initialize the unit
;++
; PY$CLONE_INIT - Initialize new PY device
;
; Functional Description:
;
;       Main thing we do here is clone up an associated terminal device
;       and initialize fields in the two new UCB's.
;
; Inputs:
;
;       R5      = Address of UCB
;
; Outputs:
;
;       All preserved
;--

PY$CLONE_INIT::

;+ ---
;       Ignore inits on UNIT #0 (the template PY UCB)
;- ---
        TSTL    UCB$W_UNIT(R2)                  ;UNIT #0??
        BNEQ    10$                             ;No: Initialize
        RSB                                     ;Yes: Return

10$:    PUSHR   #^M<R0,R1,R2,R4,R6,R7,R8>
        Bicl2   #UCB$M_DELETEUCB,UCB$L_STS(R2)  ; Clear ucbdelete - dec
        Movl    R2,R5
;
; Find the associated device.
;
; NOTE: We can't call IOC$SEARCHDEV because it expects the string to
;       be accessible from the previous access mode. (It executes the
;       prober instruction with mode=#0). I don't know how to make the
;       string accessible from the previous access mode cleanly, but I
;       do know how to move most of IOC$SEARCHDEV into the py driver.
;
        MOVAL   G^IOC$GL_DEVLIST-DDB$L_LINK,-   ; Get address of i/o database
                R8                              ; listhead
        CLRL    R6                              ; Desired mate = PTY UNIT 0
        MOVAB   L^TPSTRING,R7                   ; String address for TPA
        MOVL    #TPLENGTH,R4                    ; String length
        BSBW    SEARCHDEV                       ; Find the DDB
        BNEQ    20$                             ; Device not found
        BSBW    SEARCHUNIT                      ; Search for specific unit
        BNEQ    30$                             ; unit found
20$:    POPR    #^M<R0,R1,R2,R4,R6,R7,R8>       ; NOT FOUND: Return
        RSB

;
; Create the PTY, R1 has template UCB of TP device
;
30$:    PUSHL   R5                              ; Save R5
        MOVL    UCB$L_DDB(R5),R0                ; Find UNIT #0 UCB FOR PY DEV.
        MOVL    DDB$L_UCB(R0),R0
        MOVL    R1,R5                           ; R5 = UCB to CLONE
        JSB     G^IOC$CLONE_UCB                 ; Clone UCB

        MOVL    R2,R1                           ; Put PTY UCB back into R1
        POPL    R5                              ; Restore R5
        BLBS    R0,40$                          ; WIN!!! (big deal.)
;+ ---
;       CREATE_UCB failed, mark our PY device offline
;- ---
        BICW2   #UCB$M_ONLINE,UCB$W_STS(R5)     ; Mark offline
        BRW     100$                            ; And return
;+ ---
;       PTY UCB created successfully, link the UCBs together
;- ---
40$:    MOVL    R1,UCB$L_PY_XUCB(R5)            ; Store associated UCB
        MOVL    R5,UCB$L_TP_XUCB(R1)            ; Store the other one back
        CLRL    UCB$L_PID(R1)                   ; Clear the owner PID in PTY
        CLRW    UCB$W_REFC(R1)                  ; Reference count is ZERO
        Bicl2   #UCB$M_DELETEUCB,UCB$L_STS(R1)  ; Inhibit deletion
        MOVW    UCB$W_UNIT(R1),-                ; Set associated TP unit
                UCB$L_DEVDEPEND(R5)             ; number in PY devdepend
;+ ---
;       Call the PTY unit init routine
;- ---
        MOVL    UCB$L_DDT(R1),R0                ; Get DDT
        MOVL    DDT$L_UNITINIT(R0),R0           ; Get Unit Init Addr in DDT
        CMPL    R0,#IOC$RETURN                  ; Null Address??
        BNEQ    50$                             ; No: Call it
        MOVL    UCB$L_CRB(R1),R0                ; Yes: Look in the CRB
        MOVL    CRB$L_INTD+VEC$L_UNITINIT(R0),R0
        BEQL    100$                            ; No: Unit init routine

50$:    PUSHL   R5                              ; Save R5
        MOVL    R1,R5                           ; R5 = PTY UCB
        JSB     (R0)                            ; CALL THE UNIT INIT ROUTINE
        POPL    R5                              ; Restore R5

100$:   POPR    #^M<R0,R1,R2,R4,R6,R7,R8>
        RSB









        .PAGE
        .SBTTL  DDB finding Routines
;++
; SearchDev - Search for device DDB
;
; This routine is called to search the device database for a DDB.
; This is the first step in finding another devices UCB.
;
; This routine copied out of IOC$SEARCHDEV in IOSUBPAGD
;
; Inputs:
;
;       R8 = DDB Head
;       R7 = Address of String
;               String = ddc format: example = /TTA/
;       R4 = Length of string
;
; Outputs:
;
;       R8 = DDB of desired device if EQL, otherwise not found
;       R0 is trashed
;       R1 is trashed
;--
SEARCHDEV:                              ; Search for device name
10$:    MOVL    DDB$L_LINK(R8),R8       ; Get address of next ddb
        BEQL    20$                     ; If eql end of list
        MOVAL   DDB$T_NAME(R8),R0       ; Get address of generic device name
        MOVB    (R0)+,R1                ; Calculate len of string to compare
        CMPB    R1,R4                   ; Length of names match?
        BNEQ    10$                     ; If neq no
        CMPC    R4,(R0),(R7)            ; Compare device names
        BNEQ    10$                     ; If neq names do not match
        RSB
20$:    INCL    R8                      ; indicate search failure
        RSB

        .SBTTL  UCB finding routine
;++
; SEARCHUNIT - Subroutine to search for UCB given DDB
;
; Given the DDB of a device, get the UCB and run down the ucb list until
; we get the ucb with the desired unit number.  This code is taken out of
; IOC$SEARCHDEV in IOSUBPAGD.
;
; Inputs:
;
;       R8 = DDB of device
;       R6 = unit number of desired UCB
;
; Outputs:
;
;       R1 = UCB of device if NEQ, otherwise not found
;       R0 is trashed
;
;--
SEARCHUNIT:                             ; Search for unit number
        MOVAL   DDB$L_UCB-UCB$L_LINK(R8),-
                R1                      ; Get address of next ucb address
10$:    MOVL    UCB$L_LINK(R1),R1       ; Get Address of next ucb
        BEQL    40$                     ; If EQL then end of list
        CMPW    R6,UCB$W_UNIT(R1)       ; Unit number match?
        BEQL    30$                     ; If eql yes
        BRB     10$
30$:    MOVL    #SS$_NORMAL,R0          ; Indicate match
40$:    RSB









        .PAGE
        .SBTTL  PY$STARTIO - Device Startio routines
;++
; PY$STARTIO    - Start Input on idle device
;
; Functional Description:
;
;       If after the read FDT routines are done and nobody is doing
;       anything on the device (UCB$V_BSY = 0) then call the start io
;       routine.
;
; Called from:
;
;       Called from any one of five places:
;       - The EXE$QIODRVPKT in the PY FDT READ routine
;               which calls EXE$INSIOQ which calls IOC$INITIATE
;       - The IOC$REQCOM at the end of this PY startio routine
;               which calls IOC$INITIATE
;       - The TP startio routine which calls IOC$INITIATE
;       - The PY write fdt routine which calls IOC$INITIATE.
;               In case we must echo a character.
;       - The PY$RESUME routine which calls IOC$INITIATE.
;
; Inputs:
;
;       R3 = IRP Address
;       R5 = UCB Address
;               UCB$W_BCNT and UCB$L_SVAPTE are written by IOC$INITIATE
;
; Outputs:
;
;       R5 - UCB Address
;
;--
PY$STARTIO::
        .ENABLE LSB

        MOVL    @UCB$L_SVAPTE(R5),-             ; Initialize buffer
                UCB$L_SVAPTE(R5)                ;  pointers
PY_OUT_LOOP:
;
; Here R5 must point at the PY device UCB and not at
;  the UCB of the associated TP device.
;
5$:     TSTW    UCB$W_BCNT(R5)                  ; Any space left in rd packet
        BLEQ    50$                             ; No, Completed I/O
;
; Switch to terminal UCB
;
        MOVL    UCB$L_PY_XUCB(R5),R5            ; Set to TP ucb
;
; Look for next output in state tank
;
; Change Case statement to reflect V4 changes in routines - DEC
;
10$:    FFS     #0,#6,UCB$W_TT_HOLD+1(R5),R3
        CASE    R3,TYPE=B,<-                    ; Dispatch
                PY_PREMPT,-                     ; Send Prempt Characte - DEC
                PY_STOP,-                       ; Stop output
                PY_CHAR,-                       ; Char in tank
                PY_BURST,-                      ; Burst in progress
                >
;
; No Pending Data - Look for next character
;
        BICB    #UCB$M_INT, UCB$W_STS(R5)       ; Clear interrupt expected
;
; Call class driver for more output
;
        JSB     @UCB$L_TT_GETNXT(R5)    ; Get the next character
        CASEB   UCB$B_TT_OUTYPE(R5),#-1,#1
1$:     .WORD   PY_START_BURST-1$       ; Burst specified
        .WORD   PY_DONE-1$              ; None
        BRW     BUFFER_CHAR             ; Buffer the character
;
; Output queue exhausted
PY_DONE:
        MOVL    UCB$L_TP_XUCB(R5),R5    ; Switch UCBs to PY UCB
        BBC     #UCB$V_BSY,-            ; If not BSY then ignore
                UCB$W_STS(R5),47$       ; the char
        MOVL    UCB$L_IRP(R5),R3        ; Restore IRP
        CMPW    IRP$W_BCNT(R3),-        ; Any characters moved
                UCB$W_BCNT(R5)
        BNEQ    50$                     ; Yes complete I/O
47$:    RSB
;
; read buffer exhausted
;
50$:    MOVL    UCB$L_IRP(R5),R3        ; Restore IRP
        MOVW    #SS$_NORMAL,-           ; Set successful completetion
                IRP$L_IOST1(R3)
        SUBW    UCB$W_BCNT(R5),-        ; Update byte count
                IRP$W_BCNT(R3)
        MOVW    IRP$W_BCNT(R3),-        ; Set in status
                IRP$L_IOST1+2(R3)
;
; If we wanted to here we could set the second longword of the device status
;
        CLRL    IRP$L_IOST2(R3)         ; No status
        MOVQ    IRP$L_IOST1(R3),R0      ; Load IOSB return values

        JMP     G^IOC$REQCOM
;
; Put the character into the read buffer
;
BUFFER_CHAR:
        MOVL    UCB$L_TP_XUCB(R5),R5    ; Switch UCBs to PY UCB
        BBC     #UCB$V_BSY,-
                UCB$W_STS(R5), 60$      ; If no PY IRP, ignore
        MOVB    R3,@UCB$L_SVAPTE(R5)    ; Add character to buffer
        INCL    UCB$L_SVAPTE(R5)        ; Bump pointer
        DECW    UCB$W_BCNT(R5)          ; Show character added
60$:    BRW     PY_OUT_LOOP             ; Go for another char
;
; Take care of Burst mode R5 must be TP UCB
;
PY_START_BURST:
        BISW    #TTY$M_TANK_BURST,-     ; Signal burst active
                UCB$W_TT_HOLD(R5)
;
; Continue burst
;
PY_BURST:
        MOVL    UCB$L_TP_XUCB(R5),R1    ; Save PY UCB in R1
        CLRL    R3                      ; Initialize output size
        CMPW    UCB$W_TT_OUTLEN(R5),UCB$W_BCNT(R1)  ; Is buffer too small?
        BGTR    61$                     ; Yes
        MOVW    UCB$W_TT_OUTLEN(R5),R3  ; Nope, so output all
        BRB     62$
61$:    MOVW    UCB$W_BCNT(R1),R3       ; Just output what we can

62$:    PUSHR   #^M<R0,R1,R2,R3,R4,R5>  ; MOVC3 destroys these registers
        MOVC3   R3,@UCB$L_TT_OUTADR(R5),@UCB$L_SVAPTE(R1)
                                        ; Transfer burst to the buffer
        POPR    #^M<R0,R1,R2,R3,R4,R5>  ; Restore the registers

        ADDL2   R3,UCB$L_SVAPTE(R1)     ; Update output pointer
        SUBW2   R3,UCB$W_BCNT(R1)       ; Update output count
        ADDL2   R3,UCB$L_TT_OUTADR(R5)  ; Update input pointer
        SUBW2   R3,UCB$W_TT_OUTLEN(R5)  ; Update input count
        BNEQ    65$                     ; Not the last character
        BICW    #TTY$M_TANK_BURST,-
                UCB$W_TT_HOLD(R5)       ; Reset burst not active
65$:    MOVL    UCB$L_TP_XUCB(R5),R5    ; Swicht UCBs to PY UCB
        BRW     PY_OUT_LOOP
;
; Get a single char from tt and put in read buffer R5 = TP UCB
;
PY_CHAR:
        MOVB    UCB$W_TT_HOLD(R5),R3    ; Get the next byte
        BICW    #TTY$M_TANK_HOLD,-      ; Show tank empty
                UCB$W_TT_HOLD(R5)
        BRW     BUFFER_CHAR
;
; Stop the output R5 = TP UCB
;
; Deleted PY_STOP2 routine and changed bit clear to byte operation - DEC
;
PY_STOP:
        BICB    #UCB$M_INT, -
                UCB$W_STS(R5)           ; Reset output active
        BRW     PY_DONE                 ; DON'T go for anymore
                                        ; Or we'll get into an infinite loop
;
; Send Xon or Xoff characters, R5 = TP UCB
;
; Changed PY_XOFF and PY_XON to be PY_PREMPT - DEC
;
PY_PREMPT:
        movb    UCB$B_TT_PREMPT(r5), r3 ; Pick up the character
        BICW    #TTY$M_TANK_PREMPT,-    ; Reset Xoff state
                UCB$W_TT_HOLD(R5)
        BRW     BUFFER_CHAR
        .DISABLE LSB

PY$END:
                                        ; End of driver
        .END
$*$*EOD*$*$
$ checksum PYDRIVER.MAR
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ exit