Pup Package




The  Pup  package  consists  of a  large  body  of  Alto  software that
implements communication by means of Pups (Parc Universal  Packets) and
Pup-based  protocols.   This  software  is  broken  into  a  number  of
independent  modules implementing  various  "levels" of  protocol  in a
hierarchical  fashion.  Each  level  depends on  primitives  defined at
lower   levels,  and   defines  new   primitives   (e.g,  inter-network
addressing, process-to-process connections, byte streams)  available to
levels above it.  A program making use of the Pup package  need include
only those components implementing primitives utilized by that program.



1. Overview


This  document  is  organized   as  a  general  overview   followed  by
descriptions of each of the components of the package, with  the lowest
levels described first.  A history  of revisions to the package  may be
found at the end.

Before beginning the  real documentation, we  should like to  mention a
number of points worth bearing  in mind throughout, as well  as various
caveats and suggestions for use.

a.  This document concerns itself only with external program interfaces
and   not  with   protocol  specifications,   internal  implementation,
motivations for  design choices, etc.   The Pup package  implements the
protocols  described  in  the  memo  "Pup  Specifications"  (Maxc  file
PupSpec.Press)  and in  other documents  also to  be found  in the
 directory.  A higher-level  overview of the Pup protocols  may be
found  in  the   report  "Pup:  An  Internetwork   Architecture",  file
PupPaper.press.   Users  interested  in  protocol  information are
referred  to  those documents.   Knowledge  of these  protocols  is not
required  when  writing   programs  making  use  of   the  higher-level
primitives provided by  the Pup package (specifically,  connections and
byte streams), but is  essential when dealing directly with  the lower-
level primitives.

b.  Since both  the software and the  protocols are still  under active
development,  users  are  requested  to  avoid  making  changes  to the
package, if at  all possible.  This is  so that subsequent  releases of
the package  may be  incorporated into  existing programs  with minimum
fuss.  We have attempted to provide as general-purpose a package  as is
reasonable (consistent with clean programming practices and considering
Alto memory limitations),  so if you come  up with an  application that
simply can't  be accomodated  without modifying  the package,  we would
like to know about it.  There are a small number of parameters  that we
have designated as "user-adjustable"  and separated out into  a special
declaration file (PupParams.decl).  The intention is that users be able
to  change these  parameters and  recompile the  package;  however, one
should be aware that the  software has not been tested  with parameters
set to values other than the ones in the released version.

                             ------------
                   Copyright Xerox Corporation 1981


Pup Package                November 21, 1981                          2




c.  One of  the design goals has  been to implement software  that will
also  run  on  a  Nova.   All  Alto-specific  code  has  been carefully
separated  out  into  modules containing  "Al"  in  their  names (e.g.,
PupAlEthb.bcpl for the Alto Ethernet driver).  The Nova  equivalents of
the Alto-specific modules (released as a separate package) contain "Nv"
in their  names.  Source  files not  containing "Al"  or "Nv"  in their
names may be recompiled on the  Nova (with BCPL or the Nova  version of
Asm) and run without change; either they are completely free of machine
dependencies or (in a few cases) they enclose machine-dependent code in
conditional  compilation.   People  writing  general-purpose subsystems
making use of this package are encouraged to adopt the same approach.

d.  The Pup package makes extensive use of primitives provided  in four
other  software packages:  Context, Interrupt,  Queue, and  Timer.  The
dependence  on the  Context package  means that  calling  programs must
operate in a manner  compatible with contexts.  In particular,  the Pup
package  initiates a  number of  independent background  processes that
must  be given  an opportunity  to run  fairly frequently.   Hence, the
user's "main  program" must run  within a context,  and wait  loops and
very long computations in the main program should be  interspersed with
calls to Block.  For example, a call such as "Gets(keys)" (which causes
busy-waiting  inside  the  operating  system)  might  be   replaced  by
something like "GetKeys()", where the latter function is defined as:

        let GetKeys() = valof
           [
           while Endofs(keys) do Block()
           resultis Gets(keys)
           ]

Alternatively, you may  change the Operating System's  "Idle" procedure
to call Block, if you  understand what you are doing.  Consult  the the
Context Package writeup for further information.


1.1. Organization

The Pup software is  divided into three major levels,  corresponding to
levels 0 through 2 of the Pup protocol hierarchy.  Software at  a given
level depends on primitives provided at all levels below it.

At level 0 is the "transport mechanism" software necessary for  an Alto
to send  and receive  Pups on an  Ethernet.  This  consists of  a small
Ethernet interrupt handler that appends received Pups to an input queue
and transmits Pups taken from an output queue.  It is the  only portion
of the  Pup package specific  to the Ethernet  or to  the Alto-Ethernet
interface.   Corresponding  drivers  are  included  for  the  XEOS  EIA
interface and the  ASD Communication Processor,  for use in  Altos that
have those special interfaces installed.

Level 1 defines a number of important and generally  useful primitives.
A program desiring to send and receive "raw Pups"  (without sequencing,
retransmissions, flow control, etc.) would interface to the Pup package
at this level.  The level 1 module includes the following:

    a.   Procedures  for   creating,  maintaining,  and   destroying  a
    "socket", a process's logical connection to the Pup inter-network.

    b.  Procedures for managing  "Packet Buffer Items" (PBIs),  each of


Pup Package                November 21, 1981                          3




    which holds  a Pup  and some associated  information while  the Pup
    resides in Alto memory.

    c.   A background  process that  distributes received  Pups  to the
    correct sockets.   This includes checking  port address  fields and
    optionally verifying the Pup checksum.

    d.   Procedures for  allocating PBIs,  building Pups,  and queueing
    them for transmission.

    e.  A background process that dynamically maintains a routing table
    for transmission of Pups to arbitrary inter-network addresses.

    f.  Optional procedures permitting  the local host to be  a gateway
    (not ordinarly used).

At level 2 are  modules implementing three higher-level  protocols: the
Rendezvous/Termination Protocol (RTP), the Byte Stream  Protocol (BSP),
and  the Network  Directory  Lookup Protocol.   These  are independent,
parallel  protocols, each  built on  top of  the primitives  defined at
level 1; however, the RTP and  the BSP interact in a way such  that, in
this implementation, BSP depends on the existence of RTP.

The  RTP  module  contains   procedures  for  opening  and   closing  a
"connection" with a foreign process.  These have options permitting the
local  process  to  operate  in  the  role  of  either  "initiator"  or
"listener".

The BSP module  contains mechanisms for  sending and receiving  data by
means of error-free, flow-controlled "byte streams" between a local and
a foreign process.   These are true "streams"  in the sense  defined by
the  Alto  operating  system.   Additionally,  means  are  provided for
sending  and  receiving Marks  and  Interrupts, which  are  special in-
sequence  and  out-of-sequence  signals  defined  by  the  Byte  Stream
Protocol.  A  separate, optional module  permits sending  and receiving
blocks of data in memory an order of magnitude more efficiently than by
use of the basic "Puts" and "Gets" operations.

The Network Directory Lookup module contains procedures  for converting
between  inter-network "names"  (e.g.,  host names)  and  Pup addresses
(ports).   When necessary,  it finds  and interacts  with  some network
directory lookup  server on the  directly connected network.   The Name
Lookup  module  converts  only  from names  to  addresses,  and  may be
substituted for Network Directory Lookup when its other  facilities are
not required.


1.2. File Conventions

The Pup  package is distributed  as file PupPackage.dm,  which contains
the following binary files:


Pup Package                November 21, 1981                          4




Level 0
    PupAlEthb.br             Alto Ethernet driver (BCPL portion)
    PupAlEtha.br             Assembly code for Ethernet driver
    PupAlEthInit.br          Alto Ethernet initialization
    PupAlEIAb.br             Driver for EIA interface
    PupAlEIAa.br
    PupAlEIAInit.br
    PupAlComProcb.br         Driver for Communication Processor
    PupAlComProca.br
    PupAlComProcInit.br

Level 1
    Pup1b.br                 Main level 1 code (BCPL portion)
    PupAl1a.br               Assembly-language code for level 1
    Pup1OpenClose.br         Opening and closing Pup sockets
    PupRoute.br              Routing table maintenance and access
    PupDummyGate.br          Dummy substitute for gateway code
    Pup1Init.br              Level 1 initialization

Level 2
    PupRTP.br                Rendezvous/Termination Protocol
    PupRTPOpenClose.br       Opening and closing RTP sockets
    PupBSPStreams.br         Byte Stream Protocol (BCPL portion)
    PupBSPProt.br            Additional BSP code
    PupBSPOpenClose.br       Opening and closing BSP sockets
    PupBSPa.br               Assembly-language code for BSP
    PupBSPBlock.br           Fast BSP block transfer procedures
    PupNetDirLookup.br       Network directory lookup module
    PupNameLookup.br         Name lookup module (subset of PupNetDirLoo

The  files with  "Init"  in their  names, as  well  as PupDummyGate.br,
contain initialization  code that  need be executed  only once  and may
then be  thrown away.   (Note, however, that  the level  1 and  level 0
"Destroy" procedures are included in the "Init" modules.)

Files PupNetDirLookup, PupNameLookup.br, and the files with "OpenClose"
in their names contain  code that is infrequently executed  (i.e., only
when  particular  types  of  sockets  are  opened  or  closed)  and may
therefore  be  loaded  into  overlays (to  be  managed  by  the Overlay
package) without significant performance penalties.  All  other modules
must remain resident while any part of the Pup package is active, since
they contain main-line  code or code  that is executed  by continually-
running contexts.

Additionally,  the following  "get" files  are included.   They contain
declarations of  all structures  and other parameters  likely to  be of
interest to calling programs (as well as some others of no  interest to
callers).  We  suggest that the  user make listings  of these  files to
accompany this documentation.


Pup Package                November 21, 1981                          5




    Pup0.decl                Level 0 definitions (network-independent)
    Pup1.decl                Level 1 definitions
    PupRTP.decl              Definitions for RTP
    PupBSP.decl              Definitions for BSP

    Pup.decl                 Does "get" of all the above
    PupParams.decl           User-adjustable parameters
    PupNetDir.decl           Definitions for PupNetDirLookup
    PupStats.decl            Statistics definitions
    PupAlEth.decl            Definitions specific to Alto Ethernet
    PupAlEIA.decl            Definitions specific to EIA driver
    PupAlComProc.decl        Definitions specific to ComProc driver

A program that  does a "get"  of any of the  first group of  files must
also "get" all files earlier on  the list, and in the same  order.  (We
were not able to make  this happen automatically because of a  limit on
the number of simultaneous  open files at compilation time).   The file
Pup.decl is provided for  the convenience of programs dealing  with the
package at  the BSP level.   A "get" of  PupParams.decl is  included in
Pup0.decl, and  PupAlEth.decl and PupStats.decl  are not  ordinarily of
interest to outside programs.

The  following  table  shows,  for  each  module   (including  external
packages), what .br files constitute that module and what other modules
are also required.

Module Name             Files               Other Modules Required

BSP Block Transfer      PupBSPBlock.br      BSP
                                            ByteBlt

ByteBlt (external)      AltoByteBlt.br

BSP                     PupBSPStreams.br    RTP
                        PupBSPProt.br
                        PupBSPOpenClose.br
                        PupBSPa.br

RTP                     PupRTP.br           Level 1
                        PupRTPOpenClose.br

Net Dir Lookup          PupNetDirLookup.br  Level 1
Name Lookup             PupNameLookup.br    Level 1

Level 1                 Pup1b.br            Level 0
                        Pup1OpenClose.br    Timer
                        PupAl1a.br
                        PupRoute.br
                        PupDummyGate.br
                        Pup1Init.br

Level 0                 PupAlEthb.br        Context
                        PupAlEtha.br        Interrupt
                        PupAlEthInit.br     Queue

Context (external)      Context.br
                        ContextInit.br

Interrupt (external)    Interrupt.br
                        InterruptInit.br


Pup Package                November 21, 1981                          6




Queue (external)        AltoQueue.br

Timer (external)        AltoTimer.br

There are a few global parameters that may be changed by  modifying the
PupParams.decl file and then  recompiling the entire Pup  package.  The
most interesting  parameter is "pupDebug",  which, if true  (default is
false) causes some additional consistency checking code to be compiled.

The sources for  the Pup package  are contained in  file PupSources.dm,
and consist of the following files:

    PupAlEthb.bcpl      PupAlEtha.asm       PupAlEthInit.bcpl
    PupAlEIAb.bcpl      PupAlEIAa.asm       PupAlEIAInit.bcpl
    PupAlComProcb.bcpl  PupAlComProca.asm   PupAlComProcInit.bcpl
    Pup1b.bcpl          PupAl1a.asm         Pup1OpenClose.bcpl
    PupRoute.decl       PupRoute.bcpl
    Pup1Init.bcpl       PupDummyGate.bcpl
    PupRTPInternal.decl PupRTP.bcpl         PupRTPOpenClose.bcpl
    PupBSPStreams.bcpl  PupBSPProt.bcpl     PupBSPa.asm
    PupBSPOpenClose.bcplPupBSPBlock.bcpl
    PupNameLookup.bcpl

Additionally, there are several command files:

    CompilePup.cm            Compiles all the source files
    DumpPupPackage.cm        Creates PupPackage.dm
    DumpPupSources.cm        Creates PupSources.dm
    Pup.cm                   A list of all the source files

The source files are formatted for printing in a small fixed-pitch font
such as Gacha8 (the normal default for Empress).


1.3. Glossary of Data Types

Name     Defined in     Meaning

BSPSoc   PupBSP.decl    BSP-level  Pup  socket,  consisting  of  an RTP
                        socket   (RTPSoc)   followed    by   additional
                        information about a byte stream.  This includes
                        byte  IDs   (sequence  numbers),   queues,  and
                        allocations for incoming and outgoing  data and
                        interrupts, and a BSP stream block (BSPStr).

BSPStr   PupBSP.decl    BSP stream (part of a BSPSoc),  for interfacing
                        the  BSPSoc  to  the  Alto  operating  system's
                        stream mechanism.

HTP      Pup1.decl      Hash  Table  Preamble,  defining  the publicly-
                        accessible  operations on  a  dictionary object
                        (specifically, the  Pup routing  table).  These
                        operations  are  Lookup,  Insert,  Delete,  and
                        Enumerate.  This object is misnamed in  that it
                        need not actually be implemented by means  of a
                        hash table; at  present, the Pup  routing table
                        is not.

NDB      Pup0.decl      Network  Data  Block,   containing  information


Pup Package                November 21, 1981                          7




                        specific to each network physically attached to
                        the local host.  (A standard Alto has  only one
                        of these, for the directly-connected Ethernet.)

PBI      Pup0.decl      Packet  Buffer  Item,  which  holds  a  Pup and
                        various associated information.

PF       Pup0.decl      Packet   Filter,   controlling   acceptance  of
                        incoming packets on a given network.

Port     Pup0.decl      An   inter-network   address,   consisting   of
                        network, host,  and socket numbers,  as defined
                        by protocol.

PSIB     Pup1.decl      Pup Socket Info  Block, contains data  used for
                        setting initial default values when a PupSoc is
                        created.

Pup      Pup0.decl      An   inter-network   packet,   as   defined  by
                        protocol.

PupSoc   Pup1.decl      Level  1  Pup  socket,  defining   a  process's
                        logical  connection to  the  inter-network.  It
                        contains   default  local   and   foreign  port
                        addresses, PBI  allocation information,  and an
                        input queue header.

RT       --             Routing Table, containing information necessary
                        to route outgoing Pups to destination  hosts or
                        to gateways.  There is only one instance  of an
                        RT, called  pupRT.  The structure  of an  RT is
                        not public, but object procedures (see HTP) are
                        provided   for   accessing    and   enumerating
                        individual Routing Table Entries  (RTEs), which
                        are public structures.

RTE      Pup1.decl      Routing  Table Entry  (routing  information for
                        one network).

RTPSoc   PupRTP.decl    RTP-level Pup socket,  consisting of a  level 1
                        socket   (PupSoc)   followed    by   additional
                        information about a connection.   This includes
                        state,  connection  ID, timers,  and  a higher-
                        level Pup-handling procedure.

soc      --             An  instance of  a PupSoc,  RTPSoc,  or BSPSoc,
                        depending on context.   Note that a  PupSoc may
                        be the initial portion of an RTPSoc,  which may
                        in  turn be  the initial  portion of  a BSPSoc;
                        hence, a given soc  may be an instance  of more
                        than one of these structures.

str      --             An  instance  of  a  stream  (most   likely,  a
                        BSPStr).


Pup Package                November 21, 1981                          8




2. Level 0 Interface


Only the level 0 driver for the Ethernet is described here.  There also
exist drivers for the EIA and ComProc interfaces, but they are somewhat
specialized and are not  documented here.  Their function  is analogous
to the Ethernet driver and their operation is quite similar.

The  level  0  module (files  PupAlEthb,  PupAlEtha,  and PupAlEthInit)
serves only to interface  the Alto Ethernet to  the network-independent
Pup level 1  module.  Assuming the  level 1 code  is being used,  as is
normally the case, external programs will generally have no occasion to
deal directly with  the level 0 module.   Provisions are also  made for
sending  and receiving  non-Pup Ethernet  packets, for  use  in unusual
applications.

This module  requires the existence  of the following  external statics
(all of which are defined in level 1):

    ndbQ      A pointer to a two-word queue header  (hereafter referred
              to as  "a queue"; see  Queue Package  documentation) upon
              which the Ethernet NDB  (etherNDB) may be queued  by this
              module.   In  a  machine  with  more  than   one  network
              interface, this queue contains an NDB for each network.

    pbiFreeQ  A  queue  from  which  free  PBIs  may  be  obtained, for
              buffering received Pups.

    pbiIQ     A  queue  to  which  PBIs  are  appended  when  Pups  are
              received.

    lenPup    The maximum length of a Pup (in words).

The externally-callable procedures in this module are the following:

InitAltoEther(zone, ctxQ, device)
    Initializes  the  Alto  Ethernet  interface  and   associated  data
    structures.  "zone" is a free-storage zone from which space  may be
    obtained  for permanent  data structures  (currently less  than 100
    words).   "ctxQ" is  a queue  on which  a context  created  by this
    procedure  may  be queued.   This  procedure allocates  an  NDB and
    appends it to ndbQ;  allocates an interrupt context  (see Interrupt
    Package documentation) and sets it up to field Ethernet interrupts;
    and  allocates  and  initiates  an  ordinary  context  (see Context
    Package documentation) which  runs forever and  whose job it  is to
    restart  the Ethernet  interface  if it  is  ever shut  off  due to
    running out of free  PBIs for input.  InitAltoEther  returns having
    done  nothing  if  the  Alto  doesn't  have  an  Ethernet interface
    installed (the level 1 initialization detects the condition of ndbQ
    being empty after all interface initialization procedures have been
    called).

    "device"  should  normally be  0,  referring to  the  standard Alto
    Ethernet interface.  However, in Altos with more than  one Ethernet
    interface installed, the driver may be initialized  multiple times,
    once for each interface; in this case, device numbers 1 and 2 refer
    to the first and second additional interfaces.

EncapsulateEtherPup(pbi, pdh)


Pup Package                November 21, 1981                          9




    Encapsulates  the  Pup  contained  in  "pbi"  for  transmission  to
    physical destination host "pdh" on the directly-connected Ethernet.
    The   PBI   should   contain   a   completely    well-formed   Pup.
    EncapsulateEtherPup sets the Ethernet destination, source, and type
    fields in the  encapsulation portion of  the packet, and  also sets
    the packetLength  word in the  PBI.  SendEtherPup is  the procedure
    called from level  1 via the  encapsulatePup entry in  the Ethernet
    NDB.

SendEtherPacket(pbi)
    Queues "pbi" for  transmission on the  directly-connected Ethernet,
    and  initiates  transmission if  the  interface is  idle.   The PBI
    should contain a completely well-formed Ethernet packet (which need
    not be a  Pup), the packetLength word  in the PBI must  contain the
    physical length of the packet in words, pbi>>PBI.queue must contain
    a pointer to a queue to which the PBI will be appended after it has
    been transmitted,  and pbi>>PBI.ndb must  contain a pointer  to the
    NDB associated with the Ethernet interface through which the packet
    is to be sent.  SendEtherPacket is the procedure called  from level
    1 via the level0Transmit entry in the Ethernet NDB.

SendEtherStats(pbi, ndb) = true or false
    If the debugging version of PupAlEthb is loaded (pupDebug on), this
    procedure  copies  the  statistics  accumulated  by   the  Ethernet
    interface (described  by ndb)  into pbi and  returns true.   If the
    module  was   not  compiled   with  debugging   on,  SendEtherStats
    immediately returns false.

DestroyAltoEther(ndb)
    Turns off  the Ethernet interface  designated by ndb,  and releases
    all  storage allocated  by  InitAltoEther.  This  is  the procedure
    called from level 1 via the NDB.destroy procedure.   This procedure
    is in the PupAlEthInit module, which must therefore be  retained if
    the "destroy" operation is actually to be utilized.

When  a  packet is  received  from the  Ethernet,  the  input interrupt
routine  first  verifies that  the  hardware and  microcode  status are
correct, and discards the  packet without error indication if  not.  It
then tests the packet for acceptance by each Packet Filter (PF)  on the
Ethernet packet filter queue, as will be described shortly.  If some PF
accepts the packet, the PBI is then enqueued on the queue designated in
the PF; otherwise  it is discarded.  A  free PBI is then  obtained from
pbiFreeQ, and the receiver is restarted. (Actually, an attempt  is made
to restart the receiver before  any other processing so as  to minimize
the interval during which a packet could be missed because the receiver
isn't listening to the Ethernet.)

When an  output PBI  is passed to  SendEtherPacket, it  is queued  on a
local Ethernet output queue (eOQ,  part of the NDB).  If  the interface
is currently  idle, transmission  is initiated  immediately; otherwise,
the  PBI  is simply  left  on the  queue  for action  by  the interrupt
routine.  When an output completion interrupt occurs (or a  fatal error
indication such  as a  "load overflow", or  a 100  millisecond software
timeout), the PBI  is then enqueued on  the queue specified in  the PBI
(typically pbiFreeQ or a level 1 queue called pbiTQ).

Garden-variety errors (e.g.,  collisions, bad Ethernet CRCs,  etc.) are
handled automatically: input errors cause the received packet simply to
be discarded, while  output errors cause  retransmission.  "Impossible"


Pup Package                November 21, 1981                         10




errors (suggesting that the interface or the Alto is broken)  result in
a call to SysErr(@ePLoc, ecBadEtherStatus).

In the  debugging version  of this  module (pupDebug  on), a  number of
Ethernet performance statistics  are gathered.  These are  intended for
experimental   purposes   and   measurements.    One   should   consult
PupStats.decl to see what is collected.

Though the primary  purpose of the  Pup level 0  module is to  send and
receive Pups on a particular directly-connected network, means are also
provided for sending and receiving arbitrary  network-dependent packets
(i.e., Ethernet packets in an Alto).

Sending  a  non-Pup   packet  is  straightforward:  one   simply  calls
SendEtherPacket after constructing  the desired Ethernet packet  in the
PBI, as described above.

Discrimination among  received packets is  accomplished by one  or more
objects called Packet  Filters (PFs), which  reside on a  Packet Filter
Queue (pfQ) whose head is in the NDB.  Each PF contains a predicate and
a pointer to a queue.  When a packet is received, the predicate in each
PF in turn  is called with  the PBI as  an argument.  If  the predicate
returns true, the PBI is enqueued on the queue pointed to by the PF; if
it returns false, the next PF  is tried.  If no PF accepts  the packet,
the PBI is discarded.

The pfQ initially  contains a single PF  that accepts Pups  and appends
them to  pbiIQ (the level  1 Pup input  queue).  A program  desiring to
receive other kinds of Ethernet packets should construct its own PF and
enqueue it on the Ethernet pfQ.



3. Level 1 Interface


The  level 1  module  (files Pup1b,  PupAl1a,  PupRoute, Pup1OpenClose,
PupDummyGate, and Pup1Init) contains the mechanisms enabling  a process
to send  and receive  individual Pups  to and  from other  processes at
arbitrary inter-network addresses.   Concepts such as  "connection" and
"stream",  however,  are  not  defined at  this  level,  so  it  is the
process's  responsibility  to perform  initial  connection, sequencing,
retransmission, duplicate detection, etc., where required.

A process deals  with the level  1 module through  a PupSoc, a  level 1
socket  structure  (see  Pup1.decl),  which  completely  describes that
process's  interface  to  the  inter-network  at  the  first  level  of
protocol.  The information in the socket is as follows:

    iQ             Input  queue.   PBIs  received  for  the  socket are
                   appended to this  queue.  The two-word  queue header
                   is included  in the socket  structure itself,  so to
                   remove  a  packet  from  the  iQ  one   would  write
                   "Dequeue(lv soc>>PupSoc.iQ)".

    lclPort        Local port address (a Port structure).   This serves
                   two  purposes.  First,  the "socket  number"  in the
                   port  enables  the  level  1  Pup  input  handler to
                   distribute each incoming  Pup to the  correct PupSoc


Pup Package                November 21, 1981                         11




                   by  comparing  pbi>>PBI.pup.dPort.socket   (the  Pup
                   destination        socket        number)        with
                   soc>>PupSoc.lclPort.socket  of  each  active  PupSoc
                   until  a match  is found.   Second, the  source port
                   fields of each outgoing Pup generated by the process
                   are defaulted (if zero)  to the values given  in the
                   local port address.

    frnPort        Foreign  port  address  (a  Port  structure).   This
                   provides information for defaulting  the destination
                   port fields of outgoing Pups, in the same  manner as
                   described for lclPort.

    psib           Pup  Socket Info  Block (PSIB),  which  contains the
                   information described below.  Since it  is generally
                   the same for all sockets, there is a  "default PSIB"
                   (dPSIB) whose contents are copied into the  psib for
                   each socket when the socket is created.

    maxTPBI        The  maximum  total  number  of  PBIs  that  may  be
                   assigned to the  socket.  Since free PBIs  are taken
                   from  a  common  pool, some  means  is  required for
                   ensuring that no single socket can usurp more than a
                   certain share  of the  total available  PBIs (which,
                   aside from  reducing performance for  other sockets,
                   could lead to deadlocks in higher-level protocols if
                   the free pool became exhausted).  This  is discussed
                   further  in  the  descriptions  of  the  GetPBI  and
                   ReleasePBI procedures.

    numTPBI        The  total number  of  additional PBIs  that  may be
                   assigned  to  the socket  (i.e.,  maxTPBI  minus the
                   number of PBIs already assigned).

    maxIPBI        The maximum number of  PBIs that may be  assigned to
                   the socket for input use.

    numIPBI        The number of  additional PBIs that may  be assigned
                   for input  (i.e., maxIPBI minus  the number  of PBIs
                   already assigned for input).

    maxOPBI        The maximum number of  PBIs that may be  assigned to
                   the socket for output use.

    numOPBI        The number of  additional PBIs that may  be assigned
                   for output (i.e.,  maxOPBI minus the number  of PBIs
                   already assigned for output).

    doChecksum     If true, the Pup software checksum is checked by the
                   level  1  software in  incoming  Pups  (before being
                   given  to  the process)  and  generated  in outgoing
                   Pups.  The default value is true.

The following statics are defined within the level 1 module and  may be
referenced externally (though only a few are likely to be of interest):

    dPSIB     Pointer  to default  socket info  block, used  to provide
              initial values in part of each PupSoc when it is created.


Pup Package                November 21, 1981                         12




    gatewayIQ Pointer to queue on which received Pups not  addressed to
              this  host are  placed.   Unless the  Gateway  package is
              loaded, gatewayIQ is initialized to pbiFreeQ.

    lenPup    The length of the largest possible Pup, in words (derived
              from maxPupDataBytes).

    lenPBI    The  length of  a PBI,  in words  (derived  from lenPup).
              Note that  all PBIs  are of  the same  size and  can each
              contain a Pup of maximum length.

    lPupSoc   The length of a PupSoc, in words.

    maxPupDataBytes  The maximum  number of data  (content) bytes  in a
              Pup.  This is initialized to the  "pupDataBytes" argument
              to InitPupLevel1 and remains constant thereafter.

    ndbQ      Pointer to queue of NDBs for all the physically connected
              networks  (see level  0 description).   The first  NDB on
              ndbQ is considered to be the "default" network, i.e., the
              one  sent to  if a  process specifies  a  Pup destination
              network of zero.

    numNets   The number of directly connected networks (always 1 in an
              Alto).

    pbiFreeQ  Pointer to queue of free PBIs.

    pbiIQ     Pointer to  queue on  which incoming  Pups are  placed by
              level 0 interrupt routines.

    pbiTQ     Pointer to  queue on which  outgoing Pups  are ordinarily
              placed after transmission.

    pupCtxQ   Default context queue onto which new contexts  created by
              the Pup package will be appended.  This is initialized to
              the "ctxQ" argument to InitPupLevel1.

    pupRT     Pointer to routing table (described later).

    pupZone   Default zone from which  allocations will be made  by the
              Pup package.  This is initialized to the  "zone" argument
              to InitPupLevel1.

    socketQ   Pointer to queue of all active PupSocs.

The level  1 module  must be initialized  by calling  InitPupLevel1, as
follows:

InitPupLevel1(zone,  ctxQ, numPBI,  pupDataBytes [defaultPupDataBytes],
    numRTE [9])
    Initializes  all  the  level   1  software,  and  also   calls  the
    appropriate  level  0  initialization  (InitAltoEther  in  the Alto
    version).   "zone"  is  a free-storage  zone  from  which permanent
    allocations  may  be done.   "ctxQ"  is  a pointer  to  a  queue of
    contexts to  which the  contexts created by  this procedure  may be
    appended.  "numPBI"  is the  number of PBIs  to be  allocated (from
    "zone") and appended to the pbiFreeQ.


Pup Package                November 21, 1981                         13




    The optional argument  "pupDataBytes" specifies the  maximum number
    of data (content) bytes to be permitted in any Pup; it must be even
    and  by  convention should  not  be greater  than  532.   A smaller
    maximum Pup  length is  useful in  some applications  not requiring
    high throughput,  since the  PBIs are thereby  smaller and  one can
    have more of them at the same cost in memory.  The default value of
    this parameter is 532.

    The optional argument "numRTE"  specifies the number of  entries to
    allocate in the Pup routing  table.  These are used as a  cache for
    routing information, and there  should be at least as  many entries
    as there are likely to be simultaneous conversations with  hosts on
    different networks.

    InitPupLevel1  does the  following:  it creates  the  queues pbiIQ,
    pbiTQ,  pbiFreeQ, socketQ,  and ndbQ;  allocates "numPBI"  PBIs and
    appends them to pbiFreeQ; creates the routing table  pupRT; creates
    the  default  Pup  socket  info  block  dPSIB;  calls  the  level 0
    initialization    procedure(s);   creates    the    PupLevel1   and
    GatewayListener background  contexts (to  be described  later); and
    broadcasts  requests for  gateway routing  information.   The total
    amount of  storage taken  from "zone"  (in words)  is approximately
    numPBI*290 +  lenPSIB +  lenPupSoc +  numRTE*5 +  250 +  the amount
    needed  by level  0 initialization.   InitPupLevel1 also  calls the
    external   procedure    InitForwarder   (ordinarily    defined   in
    PupDummyGate.br),  initializes  the static  pupZone  to  "zone" and
    pupCtxQ  to  "ctxQ",  and sets  up  the  constants maxPupDataBytes,
    lenPup, and lenPBI on the basis of "pupDataBytes".

    InitPupLevel1 does not call Block, so it is permissible to  call it
    from initialization code that is not running as a context.

DestroyPupLevel1()
    Undoes the actions of InitPupLevel1.  This includes calling all the
    level 0 "destroy" procedures (via the NDB.destroy operations in all
    NDBs on  ndbQ) and  releasing all  storage allocated  from pupZone.
    DestroyPupLevel1 is in the Pup1Init module, which must therefore be
    retained rather than discarded if this procedure is to be used.

The  following  procedures  are provided  for  creating  and destroying
sockets:

OpenLevel1Socket(soc, lclPort [defaulted], frnPort  [zeroes], transient
    [false])
    Creates a PupSoc.  "soc" should point to a block of size lenPupSoc.
    "lclPort", if  specified and  nonzero, points  to a  Port structure
    describing the desired local port address.  "frnPort", if specified
    and  nonzero, points  to a  Port structure  describing  the desired
    foreign  port  address.  The  "soc"  is then  appended  to socketQ,
    thereby enabling reception of Pups directed to it.

    Each field in  the local port address  is subject to  defaulting if
    either the "lclPort"  is unspecified or the  field is zero,  in the
    following  manner.  If  the socket  number is  unspecified,  one is
    chosen at random  (it is guaranteed  unique).  If both  the network
    and  host  numbers  are  unspecified, they  are  filled  in  with a
    reasonable  local  host  address  (perhaps  based  on  the supplied
    "frnPort").  Ordinarily, one should  allow the socket number  to be
    defaulted unless one intends the process to reside at a "well-known


Pup Package                November 21, 1981                         14




    socket" (as in a server),  and one should always allow  the network
    and host numbers to be defaulted.

    If "frnPort" is unspecified, the  foreign port in the "soc"  is set
    to zeroes.  Then, if the foreign network number is  zero (generally
    for the purpose  of designating the "directly  connected" network),
    it is set to the connected network's actual number, if known.  Note
    that the  "lclPort" and  "frnPort" fields in  the "soc"  are copied
    from the corresponding arguments to OpenLevel1Socket;  the argument
    ports are not modified and are not needed thereafter.

    If  "transient"  is  true, the  caller  asserts  that  the socket's
    lifetime  will be  very short.   This modifies  the  disposition of
    incoming  Pups  that  arrive  after  the  socket  has  been closed:
    ordinarily such Pups are  answered with Error pups (saying  that no
    such  socket exists),  but  if the  socket was  transient  they are
    simply ignored.  This eliminates needless extra traffic in the case
    that the  caller broadcasts a  request through the  socket, accepts
    the  first   reply,  and  immediately   closes  the   socket.   The
    "transient"  feature  works  only if  the  local  socket  number is
    defaulted.

CloseLevel1Socket(soc)
    Causes "soc"  to be  removed from  socketQ.  This  procedure blocks
    until  all PBIs  assigned  to the  socket have  been  recovered and
    released.  If "soc" is not in fact on socketQ, this procedure calls
    SysErr(soc, ecNoSuchSocket).

Control over assignment of PBIs to sockets is accomplished in  a manner
that is  more complicated  to describe  than to  implement.  Associated
with each socket are three numbers that determine the maximum number of
PBIs  that may  be assigned  to a  socket simultaneously.   The "total"
(soc>>PupSoc.maxTPBI) is  the maximum total  number of  PBIs permitted,
while   the   "input"   and   "output"   values   (soc>>PupSoc.maxIPBI.
soc>>PupSoc.maxOPBI) determine (independent  of the overall  total) the
maximum  number  of PBIs  that  may be  assigned  for  those respective
purposes.  The "total" maximum  prevents a single socket  from usurping
more than a fixed share of  the total PBIs in the system;  within that,
the "input"  and "output"  limits, if  properly set,  prevent all  of a
socket's  allocation  from  being  devoted  to  packets  going  in  one
direction (with resultant potential deadlocks).  The "total" allocation
must be greater than either "input" or "output", but need not  be equal
to their sum, since in  most applications one expects heavy  demands on
PBIs in only a single direction.

The actual number  of PBIs assigned  to a socket  at a given  moment is
reflected  in three  other  cells in  the  socket: soc>>PupSoc.numTPBI,
soc>>PupSoc.numIPBI, and soc>>PupSoc.numOPBI.  These are initialized to
the corresponding "max" values, decremented whenever a PBI  is assigned
to the  socket, and  incremented when  the PBI  is released.   The code
responsible for allocating and releasing PBIs (the PupLevel1 background
process for input PBIs and the GetPBI procedure for output PBIs) do not
permit any of these counts to go below zero; if allocating  another PBI
would cause a count to be decremented below zero, PupLevel1 will simply
discard the Pup  and release the PBI,  and GetPBI will either  block or
fail (see below).

The  allocations in  the  socket are  also useful  when  destroying the
socket.  At  the time  CloseLevel1Socket is called,  there may  be PBIs


Pup Package                November 21, 1981                         15




that  are assigned  to the  socket but  that cannot  be located  at the
moment because they  reside on some other  queue (such as  the Ethernet
output  queue or  the  pbiTQ).  CloseLevel1Socket  simply  blocks until
soc>>PupSoc.numTPBI equals  soc>>PupSoc.maxTPBI, at  which point  it is
known that all PBIs have "returned" to the socket and been released.

PBIs may be added to the free pool simply by allocating blocks  of size
lenPBI and "Enqueue"ing them  on pbiFreeQ.  One could also  remove PBIs
from the system  by "Dequeue"ing them  from pbiFreeQ and  freeing them,
but of  course one  has no control  over which  PBIs are  available for
release.  Note that  such changes in the  total number of PBIs  are not
automatically reflected  in any  socket allocations  or in  the default
allocations contained in dPSIB.

SetAllocation(soc, total, input, output)
    Changes the  number of  PBIs that  may be  assigned to  the socket.
    "total", "input",  and "output"  are the  new maximum  values.  The
    "total"  must  be  greater than  either  the  "input"  or "output".
    SetAllocation need be called only if the desired allocations differ
    from the defaults in dPSIB.  Alternatively, one may manually change
    the contents of dPSIB; note  that the "num" and "max" values  for a
    given allocation must be  the same and that the  "total" allocation
    must  be  greater  than  or  equal  to  the  "input"  and  "output"
    allocations.  Changing dPSIB does not affect allocations in sockets
    that have already been  opened.  The initial "total"  allocation in
    dPSIB  is   numPBI-numNets,  where  numPBI   is  the   argument  to
    InitPupLevel1 that determines the number of PBIs  initially created
    and numNets is the number of directly-connected  networks (normally
    one in an Alto).  The initial "input" and "output"  allocations are
    each one less than the "total".

GetPBI(soc, returnOnFail [false]) = PBI
    Assigns  a PBI  from pbiFreeQ  and charges  it to  the  socket, for
    output use (that is, it decrements soc>>PupSoc.numTPBI  (total) and
    soc>>PupSoc.numOPBI  (output)).  If  the socket  has  exhausted its
    total or output  allocation or the  pbiFreeQ is empty,  then GetPBI
    blocks unless returnOnFail is true, in which case it  returns zero.
    The PBI returned  has its Pup header  zeroed so that if  the caller
    later transmits the Pup  without setting up source  and destination
    port addresses, the addresses will be correctly defaulted  from the
    socket.  The PBI's  "queue" pointer is  set to pbiTQ,  resulting in
    automatic release of  the PBI after  it is transmitted.   The PBI's
    "socket" pointer is set  to "soc", thereby recording the  socket to
    which it has been assigned.

ReleasePBI(pbi)
    Releases the "pbi" and appropriately credits the allocations in the
    socket to which it was assigned.

CompletePup(pbi, type [], length [])
    Causes  "pbi"  to  be  completed  and   transmitted.   "Completion"
    consists  of  the  following operations:  "type"  and  "length", if
    supplied, are stored  in the Pup type  and length fields;  any zero
    fields in the Pup source or destination ports are defaulted  to the
    values  given  in  the  owning  socket's  local  and  foreign  port
    addresses,  respectively;  the  transport  control  byte  (used  by
    gateways) is zeroed;  then, if the  socket's doChecksum flag  is on
    (the default unless changed explicitly), a software Pup checksum is
    computed and stored in the Pup.  The caller is expected to have set


Pup Package                November 21, 1981                         16




    up the Pup's ID, and contents  (if any) and its type and  length if
    not  supplied in  the  call.  Finally,  the  PBI is  routed  to its
    destination and queued for transmission.

    After transmission,  the PBI is  appended to  pbi>>PBI.queue, which
    (unless changed explicitly by the caller) will be  pbiTQ, resulting
    in automatic release of the PBI.  If a different queue is specified
    for  disposal  of the  PBI  (as is  done  in the  BSP  package, for
    example), then the caller  is responsible for keeping track  of the
    PBI, and, in particular, for ensuring that all PBIs assigned to the
    socket have been released before destroying the socket.

    A special mechanism exists for broadcasting a Pup on  all directly-
    connected networks.  If  the allNets bit is  set in the  PBI status
    word, then instead of routing the Pup to the destination  stated in
    the Pup  header, CompletePup  sends the Pup  out on  each directly-
    connected network.   For each  network, the  local host  address on
    that network is substituted for the network and host numbers in the
    Pup source port, and  the local network number is  also substituted
    for the destination network field (the checksum is  recomputed each
    time this is done).  The "queue" word in the PBI must be pbiTQ (the
    default) for this feature to work properly.

    The allNets mechanism  ordinarily causes a Pup  to be sent  on each
    directly-connected network, whether  or not the  network's identity
    is known.  However, if the  bypassZeroNet bit is also set,  the Pup
    will not be sent on networks whose identity is not known.

Distribution  of   received  Pups  to   the  correct  sockets   is  the
responsibility of  a background process  called PupLevel1.  When  a PBI
appears on  pbiIQ (where  it was left  by the  level 0  input handler),
PupLevel1 first  performs some checks  on the Pup  destination address,
and discards the PBI if it  is not destined for a process in  the local
host  (actually,  it  enqueues it  on  gatewayIQ,  which,  assuming the
PupDummyGate module has been loaded, is the same as pbiFreeQ).  It then
searches the socketQ for a socket whose local socket number matches the
Pup destination socket number.  If no such socket is found, the  PBI is
passed  to  SocketNotFound(pbi),  which  generates  an  Error  Pup  and
discards  the  packet  (but  could be  made  to  do  something  else by
clobbering  the  SocketNotFound  procedure  static  with   a  different
handling procedure).

Assuming the destination socket is found, PupLevel1 then checks the Pup
checksum (assuming the socket's doChecksum flag is on),  discarding the
PBI if it is incorrect.  Finally, the socket's "total" and  "input" PBI
allocations are checked.  If either is exhausted, the PBI  is discarded
(causing an Error Pup to  be returned to the Pup's  source); otherwise,
the allocations are updated and the PBI is appended to the socket's iQ.

PupLevel1 is also responsible for releasing PBIs on the pbiTQ, which is
the  default  queue  to  which  outgoing  packets  are  appended  after
transmission.

Another  process,  GatewayListener,  is  responsible   for  dynamically
maintaining the  routing table pupRT  and updating it  with information
periodically received from  gateways.  While routing and  routing table
maintenance are operations performed automatically (by  CompletePup and
GatewayListener),  the  format  of the  routing  table  is  of possible
interest to callers in certain cases--for example, in deciding which of


Pup Package                November 21, 1981                         17




several possible remote servers is the best choice in terms  of network
topology (see the  PupNameLookup module for  an example of  this).  The
following description is much  more than most programmers will  wish to
know about.

The  RT is  a  dictionary object  consisting of  routing  table entries
(RTEs) keyed  by network  number, each  containing information  about a
specific network.  For  a given RTE, if  the "hops" field is  zero, the
network  is  one  to  which  the  local  host  is  directly  connected;
otherwise, the network may be reached via the gateway whose host number
is given in the "host" field (the "hops" field indicates the  number of
gateways believed to lie along  the route to the destination  net).  In
either  case, the  "ndb"  field points  to  the NDB  for  the immediate
destination network (see "Pup Specifications").  If the "hops" field is
greater  than "maxHops"  (currently  15), the  network is  known  to be
inaccessible, and the remainder of the RTE should not be believed.

If no RTE exists for  a particular network, then we know  nothing about
that network and can't route Pups to it.  The routing table  is treated
as a cache  of recently-used routing  information.  When an  attempt is
made to  transmit a  Pup to a  network not  represented in  the routing
table, new routing information is obtained from a nearby gateway and an
RTE  for that  network  is inserted  into the  routing  table (possibly
displacing  some other  RTE that  has not  been used  recently).  Note,
however, that  RTEs for directly-connected  networks are  never removed
from the routing table.

Network number zero  in the routing table  is special.  It refers  to a
network known  to be directly  connected to the  local host  (but whose
identity may  or may not  be known, i.e.,  we may or  may not  know its
network  number).   Pups  handed  to  CompletePup  for  transmission to
network  zero  will  be  sent  over  this  network.   This  facility is
essential during initialization, before any gateways have  been located
and the remainder of the RT filled out.  It also  permits communication
among hosts on a network  whose identity is unknown due to  there being
no connected gateways.

The routing table as a  whole is treated as an "object",  with standard
operations  defined by  a Hash  Table Preamble  (HTP).  This  object is
misnamed, since it  need not be implemented  by means of a  hash table,
and is not in the present implementation of the Pup routing table.  The
procedures described below are merely renamed versions of the Alto OS's
Call0, Call1,  etc.  The  operations return pointers  to RTEs,  and the
caller may operate on the individual RTE by means of ordinary structure
references.  The defined operations are:

HLookup(rt, net, dontPromote [...false]) = RTE or 0
    Looks up "net"  in the routing table  "rt", returning a  pointer to
    the RTE if  it is found and  zero if not.  Unless  "dontPromote" is
    supplied and true, the RTE is marked as having been referenced most
    recently.

HInsert(rt, net) = RTE
    Inserts an RTE for "net" into "rt", setting the "net" field  of the
    RTE and zeroing the rest of the entry.  If an entry  already exists
    for "net", it  is overwritten.  If no  entry already exists,  a new
    one is created,  possibly displacing the least  recently referenced
    RTE.

HDelete(rt, net)


Pup Package                November 21, 1981                         18




    Deletes the RTE for "net" in "rt", if one exists.

HEnumerate(rt, proc, arg)
    Enumerates all RTEs in "rt", calling proc(rte, arg) for each one.

The  following miscellaneous  procedures  are of  possible  interest to
callers:

LocateNet(net) = rte or 0
    Attempts to locate  a route to "net".   If an RTE for  "net" exists
    and is valid (i.e., hops not greater than maxHops), a pointer to it
    is returned.  Otherwise, activity is initiated to locate a route to
    "net" and zero is returned.

PupError(pbi, errorType, string)
    Causes  an "Error"  Pup  to be  returned  to the  sender  of "pbi",
    containing  the specified  "errorType"  and "string".   The  PBI is
    released  in  the  process.   Consult  the  "Pup   Error  Protocol"
    specification  for  more  information.   PupError  is  called  from
    several places inside PupLevel1 when incoming Pups are rejected for
    one reason or another.

ExchangePorts(pbi)
    Exchanges the  Pup source and  destination ports in  "pbi".  Useful
    when  sending a  packet  back where  it came  from  (possibly after
    modifying its contents).

AppendStringToPup(pbi, firstByte, string)
    Appends the supplied "string" to the Pup in "pbi", starting at byte
    position pbi>>PBI.pup.bytes↑firstByte, then sets the Pup  length to
    include the data so stored.  Useful for generating Pups that end in
    (or  consist  entirely of)  a  string, such  as  Error,  Abort, and
    Interrupt Pups.

SetPupDPort(pbi, port)
    Copies the specified "port" into the Pup destination port  field of
    "pbi".

SetPupSPort(pbi, port)
    Copies  the specified  "port"  into the  Pup source  port  field of
    "pbi".

SetPupID(pbi, pupID)
    Copies the two words pointed to by "pupID" into the Pup ID field of
    "pbi".

FlushQueue(queue)
    Dequeues and releases all PBIs presently on "queue".

OnesComplementAdd(a, b)
    Returns the ones-complement sum of "a" and "b".

OnesComplementSubtract(a, b)
    Returns the ones-complement difference between "a" and "b".

LeftCycle(word, count) = result
    Returns the result of left-cycling "word" by "count" mod 16 bits.

MultEq(adr1, adr2, nWords [...2]) = true or false


Pup Package                November 21, 1981                         19




    Compares the nWords words  starting at adr1 with  the corresponding
    words starting at adr2, returning true iff they all match.

Max(a, b); Min(a, b)
    Return the arithmetic maximum or minimum, respectively, of  "a" and
    "b".  These are treated as signed integers and must differ  by less
    than 2↑15.

DoubleIncrement(adr, offset)
    Adds  the  signed  16-bit integer  "offset"  to  the  32-bit number
    pointed to by "adr".  Note that a negative "offset" will  cause the
    32-bit number to be decremented.

DoubleDifference(adr1, adr2) = value
    Returns as a  16-bit signed integer  the result of  subtracting the
    32-bit  number pointed  to by  "adr2" from  the one  pointed  to by
    "adr1".  If the two numbers differ by more than 2↑15, the result is
    either  2↑15-1  or  -2↑15,  depending on  the  sign  of  the 32-bit
    difference.

DoubleSubtract(adr1, adr2)
    Subtracts  the 32-bit  number  pointed to  by "adr2"  from  the one
    pointed to by "adr1", and leaves the result in "adr1".



4. Rendezvous/Termination Protocol Interface


The RTP module (file  PupRTP) contains primitives for  establishing and
breaking   connections  with   foreign  processes   according   to  the
Rendezvous/Termination Protocol.

The local end of a  connection is maintained within the confines  of an
RTPSoc, an RTP socket structure (defined in PupRTP.decl).   This begins
with  a  level  1  Pup  socket  (PupSoc),  but  includes  the following
additional information:

    ctx                 A pointer to the background context maintaining
                        the connection.

    state               The state of the connection (see below).

    connID              The connection ID (see "Pup Specifications").

    rtpOtherPupProc     A procedure called upon receipt of any Pup that
                        is  not  part  of   the  Rendezvous/Termination
                        Protocol.

    rtpOtherTimer       A timer for use by higher levels of protocol.

    rtpOtherTimerProc   A procedure called when rtpOtherTimer expires.

There is  some other information  (wasListening, rtpTimer) used  by the
RTP module but not of interest to external programs.

At a given moment, an RTPSoc may be in one of a number of  "states".  A
detailed explanation of  the meanings of these  states may be  found in
the memo "Pup Connection State Diagram" (file RTPStates.press).


Pup Package                November 21, 1981                         20




    stateClosed    No  connection  exists: either  none  has  ever been
                   created  or  a  previously  existing  connection has
                   terminated.

    stateRFCOut    The  local  process  has  initiated  a  request  for
                   connection (RFC) to  some foreign process.   A reply
                   is expected from the remote process.

    stateListening The local process is "listening" for an RFC from any
                   foreign process.

    stateOpen      The connection is considered by both parties to have
                   been established.  What the cooperating processes do
                   with  this connection  is a  matter  of higher-level
                   protocol (e.g., BSP).

    stateEndIn     The   foreign   process  has   requested   that  the
                   connection   be  terminated,   and  is   awaiting  a
                   confirmation from the local process.

    stateEndOut    The local process has requested that  the connection
                   be terminated, and  is awaiting a  confirmation from
                   the foreign process.

    stateDally     A transitory state having to do with the termination
                   handshake (see "Pup Specifications").

    stateAbort     The connection  has been  aborted abnormally  by the
                   foreign process.

An RTPSoc is created  by calling OpenRTPSocket, which  performs various
initialization, creates a background process to manage  the connection,
and  interacts with  some foreign  process in  one of  three  ways (see
below) to  open a  connection.  Once  the connection  is open,  the RTP
background process monitors the  socket for arrival of  Pups requesting
that the connection be closed or aborted, and updates the state  of the
socket appropriately.   The local process  may also  request explicitly
that the connection be terminated, by calling CloseRTPSocket.

The procedures defined in the RTP module are the following:

OpenRTPSocket(soc, ctxQ [pupCtxQ], openMode [modeInitAndWait], connID
          [random], otherProc [DefaultOtherPupProc], timeout
          [defaultTimeout], zone [pupZone]) = true or false
    Causes an RTP socket to be created and optional interactions with a
    foreign  process  to be  initiated.   "soc" is  a  block  of length
    lenRTPSoc which  must already  have been initialized  as a  level 1
    socket (PupSoc) by a prior call to OpenLevel1Socket.   (An external
    static "lRTPSoc" exists whose value  is the length of an  RTPSoc in
    words.) Both  the local and  foreign port addresses  (the "lclPort"
    and "frnPort" fields in the PupSoc) must be completely established,
    unless "openMode" is "listenAndWait" or "listenAndReturn", in which
    case only the local socket number (soc>>PupSoc.lclPort.socket) need
    be established.

    "ctxQ"  is a  context  queue to  which  a context  created  by this
    procedure  may be  appended.  It  defaults to  pupCtxQ  (the "ctxQ"
    passed to InitPupLevel1).


Pup Package                November 21, 1981                         21




    "openMode" specifies the  manner in which  the connection is  to be
    opened.  If  it is "modeInitAndWait",  a request for  connection to
    the  foreign process  is initiated,  and OpenRTPSocket  then blocks
    until either  the answering  RFC is  received and  the connection's
    state becomes  open (in  which case  it returns  true) or  an error
    occurs  (in  which  case the  RTPSoc  is  closed  and OpenRTPSocket
    returns  false).   If  it is  "modeInitAndReturn",  the  request is
    initiated in a similar manner, but then OpenRTPSocket  returns true
    immediately and  it is the  caller's responsibility to  monitor the
    subsequent state of the connection.

    If "openMode"  is "modeListenAndWait",  the socket  is placed  in a
    "listening" state.  When a request for connection is  received from
    some  foreign  process, a  reply  is generated  and  the connection
    becomes  open,  and OpenRTPSocket  returns  true.  If  the  mode is
    "modeListenAndReturn", OpenRTPSocket  returns true  immediately and
    it is the caller's  responsibility to monitor the  subsequent state
    of the connection.

    If  "openMode" is  "modeImmediateOpen", the  socket  is immediately
    placed in the open state (it is assumed that the caller has already
    performed  a  rendezvous with  the  foreign process  in  some other
    manner) and OpenRTPSocket returns true.

    "connID"  is  a  pointer  to  a  two-word  vector   specifying  the
    connection  ID (see  "Pup  Specifications").  If  not  specified, a
    connection  ID  is  chosen  at  random.   "connID"  need  never  be
    specified if "openMode" is one of the listening modes.

    "otherProc"  is a  procedure to  be called  when a  non-RTP  Pup is
    received  by the  socket.  This  will be  described in  more detail
    later.     If    not    specified,    "otherProc"    defaults    to
    DefaultOtherPupProc, a procedure that simply releases any PBI it is
    passed   (one   may   change   the   default   by   clobbering  the
    DefaultOtherPupProc static with something else).

    "timeout" specifies  the maximum time  OpenRTPSocket will  wait (if
    "openMode"  is  "modeInitAndWait"  or  "modeListenAndWait")  before
    timing  out  and  returning false.   It  (and  all  other "timeout"
    arguments in the Pup package) is in units of 10  milliseconds, with
    a maximum legal value of 2↑15 (a little over 5  minutes), according
    to  the   conventions  established  in   the  Timer   Package.   If
    unspecified,  "timeout"  defaults  to  "defaultTimeout",  a  static
    defined in this module, whose value in the released package is 6000
    (i.e.,    60   seconds;    this   is    set   by    the   parameter
    "defaultDefaultTimeout" in PupParams.decl).

    "zone" is a free-storage zone  from which a context block  (of size
    rtpStackSize) may  be allocated.  If  it is not  specified, pupZone
    (the "zone" passed to InitPupLevel1) is used.   Note: OpenRTPSocket
    calls InitializeContext, so the ContextInit module must be resident
    (despite what the Context Package writeup says).

CloseRTPSocket(soc, timeout [...defaultTimeout]) = true or false
    Requests  that  the  connection  rooted  in  the  RTPSoc  "soc"  be
    terminated.   If  "timeout"  is nonzero,  a  normal  termination is
    attempted if possible; if zero (or the attempted normal termination
    times  out),  the connection  is  aborted  (terminated abnormally).
    When  the  connection  has  been  closed,  the  context  created by


Pup Package                November 21, 1981                         22




    OpenRTPSocket is destroyed and  returned to the zone from  which it
    was allocated.  CloseRTPSocket then returns true if  the connection
    was  terminated  normally and  false  if abnormally.   The  level 1
    PupSoc pointed  to by "soc"  still exists, and  it is  the caller's
    responsibility to dispose of it appropriately (generally by calling
    CloseLevel1Socket).

The  process  created by  OpenRTPSocket  (called  RTPSocketProcess) has
several responsibilities.  First, all Pups arriving on the  socket's iQ
are  dequeued  and  inspected.   Ones  whose  types  are  part  of  the
Rendezvous/Termination protocol are processed internally.  All protocol
interactions  (including  replies,  retransmissions,  and  local  state
changes) are handled automatically.

Received  Pups  that  are  not  part  of  the  RTP  are  passed  to the
"rtpOtherPupProc" procedure,  which is  initialized to  the "otherProc"
argument in OpenRTPSocket.  More specifically, the statement

    (soc>>RTPSoc.rtpOtherPupProc)(pbi)

is executed,  and it  is up  to the  called procedure  to appropriately
process and  dispose of the  PBI.  Since this  call is made  within the
context of the RTPSocketProcess, which has only "rtpStackSize"  (130 as
released) words of stack space, the called procedure cannot  make heavy
demands  on  the  stack  without risk  of  stack  overflow.   One might
increase rtpStackSize (a static  defined in this module,  whose initial
value  is given  in PupParams.decl  as "defaultRTPStackSize"),  but the
safest course of action is  for the called procedure simply  to enqueue
the PBI  on some  queue looked at  by another  process with  more stack
space  available   to  it.   (One   should  note,  however,   that  the
"rtpOtherPupProc" procedure defined by the BSP module, to  be described
in the next section, manages to do all its work--a significant amount--
without  overflowing  the  RTP  process's  stack.   The  main potential
pitfall is in  calling system procedures such  as Ws that  require very
large amounts of stack space in some cases.)

"Abort" and "Error" Pups, while handled by RTPSocketProcess  (for their
effects  on   the  socket's   state),  are  also   passed  on   to  the
"rtpOtherPupProc" procedure, for purposes such as displaying  the Pup's
text to  the user.   The RTP module  distinguishes between  "fatal" and
"non-fatal" sub-types  of Errors,  treating the former  the same  as an
Abort  (thereby  placing  the  connection  in  the  "Abort"  state) and
ignoring   the   latter;   both   kinds,   however,   are   passed   to
"rtpOtherPupProc".

Additionally,  the RTPSocketProcess  checks for  expiration of  a timer
called "rtpOtherTimer"  in the  RTPSoc.  If  it expires,  the procedure
given  in  "rtpOtherTimerProc"  is  called,  with  the  socket  as  its
argument.  This facility is used in the BSP module, which also requires
the  ability  to do  asynchronous  processing.   "rtpOtherTimerProc" is
initialized to Noop when OpenRTPSocket is called.

The following miscellaneous procedures defined in the RTP module are of
possible interest to callers:

RTPFilter(pbi, checkFrnPort, checkID) = true or false
    Does selective filtering of "pbi" against parameters in  the socket
    to  which the  PBI is  assigned,  and returns  true if  the  PBI is
    accepted and false if rejected.  First, broadcast Pups (destination


Pup Package                November 21, 1981                         23




    host zero) are always rejected.  Then, if checkFrnPort is true, the
    source port  address of the  PBI is checked  for equality  with the
    foreign port address given  in the socket.  Finally, if  checkID is
    true,  the Pup  ID in  the  PBI is  checked for  equality  with the
    connection ID in the socket.

CompleteRTPPup(pbi, type, length)
    Stores "type"  and "length"  in the respective  fields of  the Pup,
    copies the connection  ID from the socket  to the Pup,  and finally
    calls CompletePup(pbi) to send it on its way.



5. Byte Stream Protocol Interface


The BSP module (files PupBSPStreams, PupBSPProt, and  PupBSPa) contains
procedures for sending  and receiving error-free,  flow-controlled byte
streams to and from a  foreign process, and for dealing with  the other
primitives defined by the BSP (namely Marks and Interrupts).

A process's interface to  the BSP module is by  way of a BSPSoc,  a BSP
socket structure, which is a further extension of an RTPSoc  (which, it
will be recalled, is an extension of a PupSoc).  The BSPSoc  contains a
large amount  of additional information,  most of which  fortunately is
not of interest to external  programs.  The items that are  of interest
are the following:

    bspStatus           A   word   containing   various   status  bits,
                        including the following three:

    markPending         A Mark has  been encountered while  reading the
                        incoming  byte  stream.   Further  attempts  at
                        input  (via  Gets  or  BSPReadBlock)  will fail
                        until this bit is cleared (either explicitly or
                        by calling BSPGetMark).

    interruptIn         An Interrupt has been received.  If  the caller
                        depends on this bit for noticing the arrival of
                        Interrupts,   then  it   must  clear   the  bit
                        explicitly after doing so.  Interrupts arriving
                        in close succession will not be distinguishable
                        as separate events unless they  are intercepted
                        via the "bspOtherPupProc"  mechanism, described
                        later.

    noInactivityTimeout This flag, normally  false, may be set  to true
                        to disable an automatic timeout  mechanism that
                        aborts  the  BSP  connection  if   the  foreign
                        process does  not respond  to any  BSP protocol
                        requests for two minutes.  The purpose  of this
                        is to detect that a connection has died (due to
                        network   failure   or   the   foreign  process
                        crashing).  Being able to disable  this timeout
                        mechanism is handy during debugging.

    bspOtherPupProc     A procedure called upon receipt of any  Pup not
                        part of the BSP (or RTP).


Pup Package                November 21, 1981                         24




    bspStr              A  block  containing  a  BSPStr,  a  BSP stream
                        structure.   This contains  the  dispatches for
                        interfacing to  the operating  system's generic
                        stream-handling  procedures (Gets,  Puts), plus
                        some information specific to the BSP stream.

A BSP  stream is  created by first  opening a  connection to  a foreign
process (by means of the RTP), then calling the following procedure:

CreateBSPStream(soc) = str
    Creates and initializes a BSP socket, and returns a pointer  to the
    stream block  within it.  "soc"  must point to  a region  of length
    lenBSPSoc (which is the  value of an external static  lBSPSoc), and
    it  must already  support one  end of  an open  RTP  connection (by
    having been passed to OpenLevel1Socket and then OpenRTPSocket).  If
    the  state  of  the  connection  is  not  stateOpen  or stateEndIn,
    CreateBSPStream returns zero.  Otherwise, the stream  is completely
    initialized  and the  pointer to  it is  returned.  See  the sample
    program at the  end of this document  for an example of  the proper
    sequence of operations for opening a BSP stream from scratch.

All the  generic stream  procedures (Gets, Puts,  etc.) must  be passed
"str"  as  an  argument,  as  should  the  procedures  BSPReadBlock and
BSPWriteBlock.  However, all other operations on the  socket (including
specialized BSP  functions such  as BSPGetMark)  must be  passed "soc".
When necessary, "str" and "soc" may be computed from each other  by the
following statements:

    str = soc+offsetBSPStr
    soc = str-offsetBSPStr

where offsetBSPStr is an external static defined in the BSP package.

The defined generic stream procedures are as follows.  The descriptions
of  Gets  and  Puts  assume  that  the  default  stream  error-handling
procedure  (invoked  by Errors(str,  ec))  is in  use;  the  real truth
appears in the description of Errors.

Gets(str, timeout [...-1]) = byte or -1
    Attempts to return the next byte from the BSP stream "str"; returns
    -1 on  any failure.  A  failure will result  if the  connection has
    become  closed  or a  Mark  has been  encountered  in  the incoming
    stream.  If "timeout" is -1 (the default), Gets  waits indefinitely
    for data to arrive (or  some failure condition to arise);  if other
    than -1, it  waits up to "timeout"  (units of 10  milliseconds) and
    then gives the failure return.

    Note  that  occurrence  of the  timeout  condition  does  not imply
    anything about the health of the connection; the timeout feature is
    provided entirely for the caller's convenience, and has  nothing to
    do  with  the  internal  connection  inactivity  timeout.   If  the
    connection  fails,  the connection  state  (soc>>RTPSoc.state) will
    change to something other than stateOpen.

Puts(str, byte, timeout [...-1]) = true or false
    Attempts to output "byte" to the BSP stream "str"; returns  true on
    success  and  false  on  failure.  A  failure  will  result  if the
    connection  has  become closed  or  the operation  times  out.  The
    "timeout"  is   defined  as   for  Gets,   with  -1   meaning  wait


Pup Package                November 21, 1981                         25




    indefinitely.  Note  that in  general, outputting a  byte to  a BSP
    stream  merely causes  that  byte to  be appended  to  a partially-
    constructed Pup  in memory;  only when a  Pup is  filled up  is any
    packet  actually  sent  over  the  net.   BSPForceOutput (described
    below) must be called to cause a partially-filled Pup to  be closed
    out and transmitted immediately.

Endofs(str) = true or false
    Returns true if there is not presently any data to be read from the
    BSP stream "str"  or a Mark has  been encountered.  Note  that this
    definition of Endofs is analogous to that for "keys" as  opposed to
    that for disk files; i.e., so long as the connection is still open,
    Endofs(str) being true says only that there is not now any  data to
    be read, not that there won't be data at some time in the future.

Closes(str) = true or false
    Closes the BSP stream "str" and destroys the associated  socket, as
    detailed in the description of CloseBSPSocket (below).

Errors(str, ec) = value
    The stream  error procedure (which  is initialized to  BSPErrors by
    CreateBSPStream) is called  under various error  conditions arising
    in  Gets  and  Puts.   The  error code  "ec"  will  be  one  of the
    following:

    ecBadStateForGets   Gets has  failed because  the connection  is no
                        longer open.  This can occur either  because an
                        Abort or fatal Error is received or because the
                        connection's  inactivity  timeout  (2  minutes)
                        expires.   (The  timeout  may  be  disabled for
                        debugging       purposes       by       setting
                        soc>>BSPSoc.noInactivityTimeout to true.)

    ecGetsTimeout       Gets   has  failed   because  no   data  became
                        available  for   reading  within   the  timeout
                        specified in the call to Gets.

    ecMarkEncountered   Gets has  failed because  it has  encountered a
                        Mark in the stream.

    ecBadStateForPuts   Puts has  failed because  the connection  is no
                        longer open.

    ecPutsTimeout       Puts has failed because it was not  possible to
                        output the byte within the timeout specified in
                        the call to Puts.

    In each case, the Gets or Puts returns the result of calling Errors
    with the  corresponding error code.   The default  Errors procedure
    returns -1 when passed any  of the Gets error codes and  false when
    passed one of the  Puts error codes, thereby obtaining  the failure
    behavior presented earlier in the descriptions of Gets and Puts.

The  remaining procedures  operate on  a "soc"  (BSPSoc) rather  than a
"str", since they are peculiar to BSP.

CloseBSPSocket(soc, timeout [...defaultTimeout]) = true or false
    Closes the connection and destroys the BSPSoc pointed to  by "soc".
    First,  if  the connection  is  still in  a  reasonable  state, any


Pup Package                November 21, 1981                         26




    pending  output  is  transmitted; CloseBSPSocket  will  wait  up to
    "timeout" for  successful acknowledgment of  this data.   Next, the
    connection  is  terminated   by  a  call  to   CloseRTPSocket  (the
    description  of which  includes the  interpretation  of "timeout").
    Then all  PBIs still  residing on the  BSPSoc's various  queues are
    released.   Finally,  the  socket   is  destroyed  by  a   call  to
    CloseLevel1Socket.  The result  returned is true if  the connection
    was closed normally, false if abnormally.

BSPGetMark(soc) = byte
    Returns the value of the pending Mark byte in the  incoming stream,
    and clears  the markPending flag  so as to  permit future  calls to
    Gets to read data past the Mark in the stream.  This procedure will
    call SysErr(soc, ecBadBSPGetMark)  if a Mark  has not in  fact been
    encountered.

BSPPutMark(soc, markByte, timeout [...-1], sendNow [false]) = true or
          false
    Inserts the specified "markByte" into the outgoing stream.  Calling
    this procedure causes all data up to and including the Mark byte to
    actually  be   transmitted  immediately.   The   interpretation  of
    "timeout" and the result returned by the procedure are the  same as
    for Puts; "sendNow" is described under BSPForceOutput (below).

BSPForceOutput(soc, sendNow [false])
    Forces   any  partially-filled   output  Pup   to   be  transmitted
    immediately.   This procedure  will never  block.  If  "sendNow" is
    true,  the BSP  package  will elicit  an  immediate acknowledgment,
    thereby expediting the process  of flushing the local  output queue
    of unacknowledged  Pups.  The  caller should  set this  argument to
    true  when  it  expects  not  to  send  more  data  for   a  while,
    particularly if it  is about to turn  around and receive  some data
    over the same stream.

BSPPutInterrupt(soc, code, string, timeout [...-1]) = true or false
    Generates a BSP Interrupt Pup (see "Pup Specifications")  using the
    specified  "code"  for  the Interrupt  Code  and  "string"  for the
    Interrupt Text.   The procedure  returns true  unless it  failed to
    send the  Interrupt due  either to the  connection no  longer being
    open or to exhausting the specified "timeout".

The BSP module accomplishes much of its work as a result of being given
control by the socket's RTPSocketProcess context through two paths: the
"rtpOtherPupProc" procedure, called when a non-RTP Pup  is encountered,
and the "rtpOtherTimerProc" procedure, called when  the "rtpOtherTimer"
expires.   These  three  cells  in  the  RTPSoc  structure  are renamed
"bspPupProc", "bspTimer", and "bspTimerProc" within the BSP module.  By
this means, the management  of both incoming and outgoing  byte streams
is   accomplished   automatically   (including   the    generation   of
acknowledgments and retransmissions).

Received Pups that are not part of either the RTP or the BSP are handed
to the  procedure given  in the "bspOtherPupProc"  cell in  the socket.
This  is  initialized  to   the  previous  contents  of   the  socket's
"rtpOtherPupProc" by  CreateBSPStream (which then  stores a  pointer to
the BSP  module's own  BSPPupProc into the  latter cell).   The earlier
description of  "rtpOtherPupProc" (in  the section  on the  RTP module)
applies to "bspOtherPupProc".


Pup Package                November 21, 1981                         27




Received Interrupt packets  are also passed to  "bspOtherPupProc" after
being processed by  the BSP module.  Note  that an Interrupt  passed in
this manner has been verified to conform to protocol (this is  the case
also for Abort and Error packets passed up from the RTP module) and may
therefore be "believed".  Any other type of packet, on the  other hand,
has had no checking done on it beyond the level 1 interface  (where the
destination port and checksum were verified).

A note on allocations:  this BSP implementation probably will  not work
at all unless the socket's PBI allocations are at least 3, 2, and 2 for
"total", "input", and  "output" respectively.  High throughput  will be
gained only by giving the socket somewhat larger allocations (say, 6 to
10 PBIs) for the direction(s) in which high throughput is desired.

In a program with at most one active BSP connection, that socket should
be allocated  all of the  PBIs in the  system except one  per directly-
connected network  (there must  always be one  extra PBI  available for
receiving  incoming  packets  on each  network);  this  is  the default
allocation established  in dPSIB by  InitPupLevel1.  In a  program with
several  active  connections,  one  should  adjust   individual  socket
allocations appropriately (though  probably not simply by  dividing the
total PBIs by the number of sockets, since doing so typically  leads to
underutilization of PBIs).   Assuming there are  plenty of PBIs  in the
system,  it  is  generally  safe  to  overcommit  the  system resources
(relying  on  the  statistical  unlikelihood  that  all   sockets  will
simultaneously  tie up  all  the PBIs  to which  they  are individually
entitled).   One  should  be  aware,  however,  that  the  higher-level
protocols  can  get into  deadlock  conditions if  the  system pbiFreeQ
becomes exhausted.  For  the same reason, a  PBI passed to  an external
program via the "bspOtherPupProc" entry in the socket must  be released
as  quickly  as possible,  since  it is  charged  against  the socket's
allocation.

The BSP module includes a static "bspVersion" whose value  is (protocol
version * 1000) + package version.



6. BSP Block Transfer Procedures


The BSP stream mechanism just presented, while being a "fast stream" in
the sense defined by the operating system, is still relatively slow and
is  therefore not  well suited  to transferring  large volumes  of data
(such  as file  transfers  between disk  and net).   A  separate module
(PupBSPBlock) is provided for accomplishing block transfers at least an
order of magnitude faster than by iterated calls on Gets or Puts.  This
module requires  that the  AltoByteBlt module  (released as  a separate
package) be loaded as well.

Two procedures are defined in this module:

BSPReadBlock(str, wordP, byteP, count, timeout [...-1]) = count
    Reads a maximum of "count" bytes from the BSP stream "str", storing
    them in memory starting  at byte position "byteP" relative  to word
    address "wordP" (for example, byteP = 0 means the left byte  of the
    word referenced by "wordP").  The transfer terminates under  any of
    the  conditions that  would cause  Gets(soc,timeout) to  return -1.
    The procedure returns the actual number of bytes transferred.


Pup Package                November 21, 1981                         28




BSPWriteBlock(str, wordP, byteP, count, timeout [...-1]) = count
    Writes  a  maximum  of  "count"  bytes  to  the  BSP  stream "str",
    obtaining  them  from  memory  starting  at  byte  position "byteP"
    relative to  word address "wordP".   The transfer  terminates under
    any of  the conditions that  would cause  Puts(soc,byte,timeout) to
    return false.   The procedure  returns the  actual number  of bytes
    transferred.



7. Network Directory Lookup Module


This  module  (file  PupNetDirLookup)  contains  procedures  to convert
between  a string  consisting of  any legal  inter-network name/address
expression and a  Port structure containing the  corresponding address.
See  the  memo  "Naming  and  Addressing  Conventions  for  Pup"  (file
PupName.press) for information on legal expressions.

The PupNetDirLookup module contains all the procedures described below.
The alternative PupNameLookup module,  included in the Pup  package for
historical reasons, contains only GetPartner and  ParseAddressConst; it
is  somewhat  smaller  than  PupNetDirLookup, and  it  may  be  used in
situations where the other functions are not needed.

GetPartner(name, stream [none], port, s1 [...none], s2 [...none]) =
          true or false
    Parses  the BCPL  string "name"  and stores  the  resulting address
    value in  the Port structure  "port", returning true  if successful
    and false otherwise.  "stream", if nonzero, is used  for publishing
    an error message if the conversion is unsuccessful.  "s1" and "s2",
    if supplied, specify the  high- and low-order parts of  the default
    socket number, which is  substituted into the "port" if  the socket
    number is unspecified in the "name".

    If the "name" consists  entirely of address constants (in  the form
    "net#host#socket" or some subset thereof, where the  components are
    octal numbers), then  it is parsed locally.   Otherwise, GetPartner
    attempts  to  establish  contact with  a  Network  Directory Lookup
    server, to which it passes the "name" for evaluation.  If the reply
    consists of several alternative addresses, the "best" one is chosen
    on the basis of information in the local routing table.  Regardless
    of whether  or not  the string is  an address  constant, GetPartner
    will return false (with the message "Can't get there from here") if
    no routing table entry exists for the resulting network and several
    calls to LocateNet discover no way of reaching that network.

ParseAddressConst(name, port) = true or false
    Attempts to parse the BCPL string "name" as an address  constant of
    the  form  "net#host#socket".   Stores  the  result  in  "port" and
    returns true if successful; returns false if unsuccessful.

EnumeratePupAddresses(name, filter [TakeTheFirst], arg [], dontCheckRT
          [false]) = 0 or error code
    Evaluates "name" to one or more addresses.  For each address, calls
    filter(port,  arg), where  port is  a pointer  to a  Port structure
    containing  that   address  and   arg  is   the  "arg"   passed  to
    EnumeratePupAddresses.  If filter returns true,  enumeration stops;
    if false, enumeration continues.


Pup Package                November 21, 1981                         29




    Ordinarily  the addresses  are  enumerated in  order  of increasing
    distance away from here (hop count), and inaccessible addresses are
    omitted;  however,  if  "dontCheckRT"  is  supplied  and  true, the
    addresses are not sorted and are all returned.  Filter  defaults to
    an internal procedure TakeTheFirst, which treats "arg" as a pointer
    to a Port and copies the first Pup address to that port, discarding
    the rest.

    EnumeratePupAddresses returns zero if successful and an  error code
    (from  PupNetDir.decl)  if  unsuccessful.   The  error   codes  are
    ecNameNotFound,  meaning that  the name  is not  registered  in the
    Network Directory  server data  base; ecCantGetThere,  meaning that
    the name exists but none of the addresses are  currently accessible
    in the internet;  and ecNoServerResponded, meaning that  no Network
    Directory server could be located.

PupAddressLookup(port, lvEC [], zone [pupZone]) = string or 0
    Interacts with  a network  directory server to  find a  name string
    corresponding  to  the  Pup  address  pointed  to  by  "port".   If
    successful, returns a string allocated from "zone"  (which defaults
    to pupZone,  the zone passed  to InitPupLevel1).   If unsuccessful,
    stores an error code  (from PupNetDir.decl) at @lvEC (which  may be
    omitted) and returns zero.  The error codes  are ecAddressNotFound,
    meaning that the address is not registered in the Network Directory
    server data base; and ecNoServerResponded, meaning that  no Network
    Directory server could be located.



8. Example


The  following example  program  makes use  of most  of  the facilities
provided in  the Pup  package.  It is  basically a  rock-bottom minimal
user Telnet (like Chat) with no redeeming features whatsoever.

The main procedure  PupExample performs initialization,  which consists
of  creating a  large zone,  initializing the  Pup package,  creating a
large display window, and  creating and starting a context  running the
procedure TopLevel.

TopLevel first requests the user to type in a foreign port  name, which
it  parses  by  calling  GetPartner (note  that  the  socket  number is
defaulted to 1,  the server Telnet socket).   Then a socket  is created
and a connection is opened.  Two new contexts are now  created, running
the  procedures KeysToNet  and  NetToDsp.  TopLevel  then  blocks until
either the connection is no longer open or the second blank key  on the
right of the  keyboard is pressed, at  which point it destroys  the two
contexts  it created,  closes  the connection,  and loops  back  to the
beginning.

The KeysToNet procedure blocks waiting for keyboard input, then outputs
the typed-in character  to the BSP  stream and calls  BSPForceOutput to
force  immediate transmission.   If  the Puts  fails,  KeysToNet simply
blocks forever, in the  expectation that TopLevel will detect  that the
connection is no longer open and take appropriate action.

The NetToDsp procedure  blocks waiting for  input from the  BSP stream.
When a normal character is  received, it is output to the  display.  If


Pup Package                November 21, 1981                         30




Gets returns -1,  then either a Mark  is pending or the  connection has
ended; if the former, a message is printed and BSPGetMark is  called to
clear  the  Mark  pending  status;  if  the  latter,   NetToDsp  blocks
indefinitely.


// PupExample.bcpl

// Bldr PupExample PupBSPStreams PupBSPProt PupBSPa PupBSPOpenClose ↑
//  PupRTP PupRTPOpenClose PupNameLookup ↑
//  Pup1b PupAl1a Pup1OpenClose PupRoute ↑
//  PupAlEthb PupAlEtha ↑
//  Context ContextInit Interrupt AltoQueue AltoTimer ↑
//  Pup1Init PupDummyGate PupAlEthInit InterruptInit

get "Pup.decl"

external
[
InitPupLevel1; OpenLevel1Socket; CloseLevel1Socket; SetAllocation
OpenRTPSocket; CreateBSPStream; GetPartner
BSPForceOutput; BSPGetMark
InitializeContext; CallContextList; Block; Enqueue; Unqueue
InitializeZone; CreateDisplayStream; ShowDisplayStream
Gets; Puts; Closes; Endofs; Ws
keys; dsp
]
static [ ctxQ; myDsp; bspSoc; bspStr ]


let PupExample() be  // initialization
[
let myZone = vec 10000; InitializeZone(myZone, 10000)
let q = vec 1; ctxQ = q; ctxQ!0 = 0
InitPupLevel1(myZone, ctxQ, 20)
let v = vec 10000
myDsp = CreateDisplayStream(40, v, 10000)
ShowDisplayStream(myDsp)
let v = vec 3000
Enqueue(ctxQ, InitializeContext(v, 3000, TopLevel))
CallContextList(ctxQ!0) repeat
]


and TopLevel() be  // top-level process
[
Ws("*nConnect to: ")
let name = vec 127; GetString(name)
if name>>String.length eq 0 then finish
let frnPort = vec lenPort
unless GetPartner(name, dsp, frnPort, 0, 1) do loop
let v = vec lenBSPSoc; bspSoc = v
OpenLevel1Socket(bspSoc, 0, frnPort)
unless OpenRTPSocket(bspSoc, ctxQ) do
   [ Ws("*nFailed to connect"); CloseLevel1Socket(bspSoc); loop]
Ws("*nOpen!")
bspStr = CreateBSPStream(bspSoc)
let keysToNetCtx, netToDspCtx = vec 1000, vec 1000
Enqueue(ctxQ, InitializeContext(keysToNetCtx, 1000, KeysToNet))
Enqueue(ctxQ, InitializeContext(netToDspCtx, 1000, NetToDsp))


Pup Package                November 21, 1981                         31




Block() repeatuntil bspSoc>>BSPSoc.state ne stateOpen %
   @#177035 eq #177775  //second blank key pressed
Unqueue(ctxQ, keysToNetCtx); Unqueue(ctxQ, netToDspCtx)
Closes(bspStr)
Ws("*nClosed!")
] repeat


and KeysToNet() be
[
test Puts(bspStr, GetKeys())
   ifso BSPForceOutput(bspSoc)
   ifnot Block() repeat
] repeat


and NetToDsp() be
[
let char = Gets(bspStr)
if char eq -1 then
   test bspSoc>>BSPSoc.markPending
      ifso
         [
         Ws("*nI saw a Mark!")
         BSPGetMark(bspSoc)
         loop
         ]
      ifnot Block() repeat
Puts(myDsp, char)
] repeat


and GetKeys() = valof
[
while Endofs(keys) do Block()
resultis Gets(keys)
]


and GetString(string) be
[
for i = 1 to 255 do
   [
   let char = GetKeys(); Puts(dsp, char)
   test char eq $*n
      ifnot string>>String.char↑i = char
      ifso [ string>>String.length = i-1; return ]
   ]
]



9. Revision History


March 25, 1976

Various  minor bugs  in both  code and  documentation were  fixed.  One
serious  error  in  the   documentation  was  in  the   description  of


Pup Package                November 21, 1981                         32




CreateBSPStream, where "lenBSPStr"  should have been  "lenBSPSoc".  The
level 1, RTP,  and BSP modules  each became slightly  smaller.  Various
calls to CallSwat were changed to SysErr with registered error codes.

Level 0: External change: file PupAlEth.bcpl replaced by PupAlEthb.bcpl
and  PupAlEtha.asm.  Internal  change: fast  (~20-instruction) Ethernet
receiver turnaround implemented.

Level  1:  External   changes:  statics  pupZone  and   pupCtxQ  added;
procedures SetPupDPort, SetPupSPort, SetPupSPort, and FlushQueue added;
RT structure definition changed; default pupErrSt is now a "nil" stream
rather than "dsp".

RTP:  External changes:  defaultTimeout and  rtpStackSize  changed from
manifests  to statics  (with default  values  defaultDefaultTimeout and
defaultRTPStackSize); DefaultOtherPupProc added.

BSP: External  change: static bspVersion  added.  Internal  change: the
transmission strategy was  modified to elicit an  acknowledgment before
allocation is completely exhausted, hence reducing lost  throughput due
to round-trip delay.

April 16, 1976

The released package Pup.dm was renamed PupPackage.dm, and  a debugging
version  of the  package  released as  PupDebug.dm.  A  number  of bugs
(particularly in level 1) were uncovered while bringing up the software
on the Nova.

Level 0: External change:  lenPup and lenPBI changed from  manifests to
statics  (defined  in level  1)  to permit  changing  PBI  size without
recompiling  the  package.  Internal  change:  100-millisecond transmit
timeout and discard added (eliminating deadlocks caused by  things like
disconnecting the Alto from the Ethernet).

Level 1: External changes:  gateway code split out into  separate files
PupGateway and PupDummyGate, one  of which must be loaded  (usually the
latter); optional extra argument "pupDataBytes" added to InitPupLevel1;
default allocations in dPSIB changed  to permit a socket to  assign all
but  one  of the  PBIs  in the  system;  OpenLevel1Socket  defaults the
foreign net in some  circumstances.  Internal change: if  "pupDebug" is
on, PupLevel1 checks for the pbiFreeQ being exhausted for more  than 20
seconds and calls Swat (this usually indicates a deadlock).

BSP: External change:  PupBSPb.bcpl replaced by  PupBSPStreams.bcpl and
PupBSPProt.bcpl  (necessitated  by  Nova  BCPL's  inability  to compile
PupBSPb in one gulp).

May 18, 1976

Mostly  bug  fixes   and  performance  improvements.    Some  structure
definitions were changed, so recompilation of user programs is advised.

Level 0: Internal changes: more assembly code included to reduce packet
loss rate; performance statistics gathered if pupDebug on.

Level 1: External change: optional "type" and "length"  arguments added
to CompletePup.

October 6, 1976


Pup Package                November 21, 1981                         33




Significant internal changes were made  at levels 0 and 1,  and several
new capabilities were  added.  However, for  the most part  the changes
are  upward-compatible.    Many  structure  declarations   changed,  so
recompilation of programs that "get" any Pup .decl files is required.

Level  0: External  changes: SendEtherPup  removed; EncapsulateEtherPup
and  SendEtherPacket  added;  ability  to  send  and  receive  non-Pups
implemented.

Level  1:  External  changes: PupRoute  file  added;  PupGateway module
deleted  from  public  Pup package  release;  routing  table completely
reorganized;  new  procedures  HLookup,  HInsert,  HDelete, HEnumerate,
HHash added; pupErrSt removed; mechanism added for broadcasting  to all
connected   networks;  procedures   DoubleIncrement,  DoubleDifference,
Double subtract  included (formerly in  BSP module).   Internal change:
GatewayListener dynamically maintains the best path to each network and
purges  RTEs of  networks  for which  no routing  information  has been
received recently.

BSP: Internal  change: adaptive  retransmission timeout  implemented to
reduce packet loss rate when  sending through slow networks or  to slow
destinations (e.g., Maxc).

March 21, 1977

Mostly bug fixes.  Some structure definitions at level 0  were changed,
so recompilation of user programs is advised.

Level 0: SendStats operation added to the NDB object.

July 11, 1977

No external  changes.  The Ethernet  driver was rewritten  to eliminate
several  low-probability   race  conditions  and   improve  performance
slightly.   The  driver  now  uses  the  "input  under  output" feature
unconditionally, so problems may be encountered on Alto-Is  running old
microcode.

March 20, 1978

External changes:  Several source files  have been broken  into smaller
pieces to  permit much  of the code  to be  included in  overlays.  The
added modules  are Pup1OpenClose, PupRTPOpenClose,  and PupBSPOpenClose
(see section 1.2 for revised packaging information).   Recompilation is
required of any programs that get Pup.decl or PupBSP.decl.

Internal changes: BSP performance  through slow links and  gateways has
been improved.  GetPartner's timeout  has been increased.  A  few minor
bugs have been fixed.

November 6, 1978

Level 0: External changes: The Ethernet driver can now control multiple
interfaces  connected  to  the  same  Alto.   InitAltoEther  is  called
differently.  Drivers are now available for the XEOS EIA  interface and
the ASD Communication Processor, though they aren't documented here.

Level  1:  Internal  change: The  routing  module  data  structures and
algorithms have  geen modified  to conform to  some minor  Pup protocol
changes.


Pup Package                November 21, 1981                         34




BSP:  External  change:  An  inactivity  timeout  has  been   added  to
automatically abort connections that have died; this may be disabled by
setting the  noInactivityTimeout bit in  the BSPSoc.   Recompilation is
required of any programs that get Pup.decl or PupBSP.decl.

February 19, 1979

Level 0:  Internal change:  The format of  the statistics  collected by
drivers changed.

Level  1:  Internal  change: The  interface  between  PupRoute  and the
forwarder changed.   This is  only of  interest to  gateways.  External
change: The definitions of  pup types and well-known sockets  have been
removed from Pup1.decl.  We now  feel that these belong in  less global
declaration  files  closer   to  the  code  implementing   the  various
protocols.   Recompilation  of   user  programs  will   probably  cause
undefined symbols which you will have to add to your declaration files.

May 27, 1979

Level 1: External  changes: InitPupLevel1 takes an  additional optional
argument   "numRTE";   LocateNet   procedure   added;   HHash  removed;
PupDummyGate  module  moved from  resident  to  initialization; lPupSoc
static added.  Internal  changes: the routing table  is now a  cache of
routing information rather than a hash table of all accessible networks
in the internet; RTEs may  be "invalid" (hops greater than  maxHops and
ndb equal to zero); new PupRoute.decl includes definitions  internal to
the routing module.

RTP: External change: lRTPSoc static added.  Internal change: more code
moved from resident (PupRTP) to swappable (PupRTPOpenClose) modules.

BSP: External change: lBSPSoc and offsetBSPStr statics added.  Internal
changes: minor bugs fixed; more code moved from resident (PupBSPStreams
and PupBSPProt) to swappable (PupBSPOpenClose) modules.

NameLookup: External  changes: RequestNameLookup  and ParseAddressConst
procedures now exported.

March 9, 1980

Levels 0 & 1: External change: a "destroy" operation has been  added to
the NDB, and  the procedure DestroyPupLevel1  is included to  shut down
the Pup package.  A few bugs have been fixed.

December 30, 1980

BSP:  External  change:   The  default  timeouts  for   Puts,  PutMark,
PutInterrupt and BSPWriteBlock were changed from 'defaultTimeout' to '-
1' (infinity).   This makes the  'put' operations symmetrical  with the
'get' operations: so long as the other end of the connection  is alive,
the BSP will wait indefinitely to put or get a byte.   Recompilation of
client programs is not necessary.

January 25, 1981

BSP:   External   change:   optional   'sendNow'   argument   added  to
BSPForceOutput and BSPPutMark.  Recompilation of client programs is not
necessary.


Pup Package                November 21, 1981                         35




November 21, 1981

Level  1:  External  change:  optional  "transient"  argument  added to
OpenLevel1Socket.

NameLookup: External change: a new module PupNetDirLookup  provides the
same functions as PupNameLookup (which still exists),  and additionally
contains  new  procedures  EnumeratePupAddresses  and PupAddressLookup.
Recompilation of client programs is not necessary.