!********************************
!*  emas-2900  fep  itp server  *
!*   file: itps9/itps9y         *
!*   date: 25.mar.82             *
!********************************
!! stack size = 500
!*
!*  code options
!*  a = additions
!*  b = kent booking server code
!*  n = ercc nsi
!*  r = ring
!*  k = kent
!*  e = ercc
!*  x = transport service
#if (k&e)!(x&n)!(x&r)!(r&n)!(k&n)!~(x!r!n)!~(k!e)
#report "Options incompatible"
#abort
#fi
!*

control  1
include  "deimosperm"

begin 

#datestring
#timestring
      conststring  (13)vsn = "itps...9(x)h"
      recordformat  am1f(integer  rxs, rxd, txs, txd)

      ownrecord  (am1f) name  l == 1;         ! addr passed by eam1

      !!   no of data bytes in a short block  

#if ~k
      constinteger  small block max = 51;          ! 64-4-6-4
       constinteger  big block max = 127;   ! < 256 !
#else
      constinteger  small block max = 110;          ! 128-4-6-4
       constinteger  big block max = 220;   ! < 256 !
#fi

      constintegername  no of big == k'100112'; ! no of free buffs
      constintegername  no of small == k'100114'
      owninteger  critical = 15;        ! switch off o/p level


      recordformat  itpf((byteinteger  cnsl, hdb1, hdb2, c 
        (string  (241) s or  byteintegerarray  a(0:241)) or  c 
         byteintegerarray  aa(0:244)))

#if r&e
      recordformat  lev3f(bytesrray  reserved(0:7), integer  uflag,
        record  (itpf) itp)
#fi
#if n
      recordformat  lev3f(bytearray  reserved(0:5), c 
        record  (itpf) itp)
#fi
                                 ! nb: replaces fn,sufl,st,ss,flag,uflag

#if x!(k&r)
      recordformat  lev3f(bytearray  reserved(0:7), record  (itpf) itp)
#fi

      recordformat  mef(record  (mef) name  link, c 
        byteinteger  len, type, (record  (lev3f)lev3 or  c 
         bytearray  params(0:231)))

      recordformat  m2900f(record  (mef) name  l, byteinteger  len, type, c 
        integer  stream, sub ident, c 
         p2a, p2b, p3a, p3b, p4a, p4b, p5a, p5b, p6a, p6b)

      recordformat  m2900bf(record  (mef) name  l, byteinteger  len, type, c 
        integer  stream, sub ident, c 
         byteintegerarray  b(0:19))

      recordformat  m2900if(record  (mef) name  l, byteinteger  len, type, c 
        integer  stream, sub ident, p2a, p2b, string  (15) int)

      recordformat  m2900cf(record  (mef) name  l, byteinteger  len, type, c 
         integer  stream, sub ident, integerarray  pa(0:9))


      recordformat  maf(record  (mef) name  l, byteinteger  mlen, c 
        mtype, byteintegerarray  a(0:240))

#if ~x
      recordformat  pe(byteinteger  ser, reply, c 
        fn, gate port, record  (mef) name  mes, (byteinteger  c1, c2 or  c 
         integer  c))
#else

      recordformat  pe(byteinteger  ser, reply, c 
       (integer  a, b, (integer  c or  byte  c1, c2) or  byte  fn, a2, c 
         (record (mef)name  mes, byte  gate port, task port or  c 
         string  (3) facility)))
#fi


      recordformat  qf(record  (mef) name  e)

      !********************************************************
      !*  formats of tables, ie stream descriptors, tcps etc  *
      !********************************************************
      recordformat  con desf(record  (mef) name  hold, c 
        integer  state, stream, byteinteger  o state, out go, c 
        in cnt, tcp, cnsl, seq bits, pmt n, mode, hold f, abortf, c 
#if k
        gahs wanted, gahs sent,
#fi
        integer  trig, i pos, opos, o lim, o trig, p lim, c 
        in lim, out lim, o posx, (record  (mef) name  in mes or  c 
              record  (qf) inp q))

      recordformat  cons statef(record  (con desf) name  con des)

#if ~k
#if a
      recordformat  tcpf(integer  state, con state ind, c 
         held, h ind, h no, byteinteger  port, ostate, tcpn, node, term, c 
         size, max, en in,en in count, record  (qf) outq)
#else
      recordformat  tcpf(integer  state, con state ind, c 
         held, h ind, h no, byteinteger  port, ostate, tcpn, node, term, c 
         size, max,  record  (qf) outq)
#fi
#else
#if ~a
      recordformat  tcpf(integer  state, con state ind, c 
         held, h ind, h no, byteinteger  port, ostate, tcpn, node, term, c 
         size, max,  string (8) name, record  (qf) outq)
#else
      recordformat  tcpf(integer  state, con state ind, c 
         held, h ind, h no, byteinteger  port, ostate, tcpn, node, term, c 
         size, max,  en in,en in count, string (8) name, record  (qf) outq)
#fi
#fi
      !************************************************************
      !*  upper level (itp&rje) handler messages to gate
      !************************************************************
#if ~x
      constinteger  enable facility = 1;  ! enable the facility
   !    %constinteger disable facility = 2;   ! the reverse
      constinteger  call reply = 3;     ! reply to a 'call connect'
      constinteger  enable input = 4;   ! allow a block to be read
      constinteger  put output = 5;     ! send a block of output
      constinteger  close call = 6;     ! terminate a call
      constinteger  abort call = 7;     ! abort the call
   
      constinteger  reject = 0;         ! qualifier on above
      !**********************************************************
      !*  messages from gate to upper level protocols
      !**********************************************************
      constinteger  incoming call = 2
      constinteger  input here = 3;     ! block arrived from node
      constinteger  output transmitted = 4;  ! prepared to accept more
      constinteger  call closed = 5;    ! either end has closed down
      constinteger  call aborted = 6;   ! other end has aborted
   
#else
#if k
      include  "tsbsp_tscodes"
#else
       include  "b_ygatecalls"
#fi
#fi

      !**************************************************************
      !*         buffer manager calls   (from and to)               *
      !**************************************************************
!      %constinteger buffer here = 0
      !********** to buffer manager ***********
      constinteger  request buffer = 0
      constinteger  release buffer = 1
      !**************************************************************
      !*             calls to 2900 link handler                     *
      !**************************************************************
      constinteger  send data = 0
      constinteger  low level control = 1
      constinteger  here i am = 2
      constinteger  return control = 3

      !**************************************************************
      !*               replies from 2900 link handler                 *
      !****************************************************************
      constinteger   interf addr = 0
      constinteger  do input = 1
      constinteger  do output = 2
      constinteger  message = 3
      constinteger  mainframe up = 4
      constinteger  mainframe down = 5
      !****************************************************************

#if b
      ! booking server messages

      constinteger  logged off = 1
      constinteger  can i logon = 2

      !from booking server

      constinteger  logon reply = 1
      constinteger  force off = 2

      !flag values for logon reply

      constinteger  bkaccept = 2
      constinteger  bkreject = 1

#fi
      !********** various service numbers *************
#if ~x!k
      constinteger  gate ser = 16
#else
       constinteger  gate ser = 24
#fi
      constinteger  buffer manager = 17
#if b
      constinteger  host bk ser = 25
#fi

      constinteger  link handler = 18

      constbyteintegername  change out zero == k'160310'
      constinteger  t3 ser = 21

      constinteger  comm bit = k'1'
      constinteger  accept char = k'002'
      constinteger  acfy = k'010';     ! peter calls it rxfy
      constinteger  xopl = k'020';     ! x operable - latched
!      %constinteger xop  = k'040';     ! x operable
      constinteger  ready = k'200'
      !************************************************************
      !*                   tcp  states                            *
      !************************************************************
      ! %constinteger not allocated = 0
      constinteger  connected = 1
#if x
      constinteger  tcp disc = 2
#fi
      !******  tcp_ostate states  (permission to send)  *****
      constinteger  idle = 0
      constinteger  busy = 1
      !***********************************************************
      !*               2900  states                               *
      !***********************************************************
      own  integer  host state = 0;      ! holds 2900 state
      constinteger  down = 0
      constinteger  up = 1
      !****************** comms control states ********************
!      %constinteger unused = 0
      constinteger  disconnecting = 1
      constinteger  connecting = 2
      constinteger  suspending = 4
      constinteger  aborting = 5
      constinteger  enabling = 7
!      %constinteger enabled = 8

      constinteger  fixed = 10;         ! 1st available stream
      !**************************************************************
      !*            console states                                  *
      !**************************************************************
      constinteger  not allocated = 0
      constinteger  name sent = 1;      ! hello has been received
      constinteger  pass sent = 2;      ! 'name' has been received
      constinteger  logging on = 3
      constinteger  logged on = 4;      ! 2970 has accepted it
      constinteger  input enabled = 5
      constinteger  logging off = 6;    ! 2970 is getting rid of it
      constinteger  logging off 2 = 7;   ! waiting to send it

      !!  ostate  states

      !! %constinteger idle = 0
      constinteger  enabld = 1
      constinteger  out p = 2;          ! output req pending
      constinteger  pmt p = 4;          ! prompt request pending
      !bit values to indicate if input/output aborted in d_abortf
      constinteger  input aborted = 1
      constinteger  output aborted = 2

      !**********************************************************
      !*   itp  header  bytes  definitions                      *
      !**********************************************************
      constinteger  text = 0;           ! in itp_hdb1
      constinteger  bin b = 1
      constinteger  control = 1
      constinteger  go ahead = 2;    ! in itp_hdb1
      constinteger  hello = 8
      constinteger  i disconnect = 4

      constinteger  terminated = 2;  ! in itp_hdb2
      constinteger  prompt = 4
!      %constinteger text marker = 8
      constinteger  seq no valid = 32
!      %constinteger seq no bits = x'c0'
      constinteger  seq inc = x'40'

      constinteger  intm = 1;            ! hdb2 - control message
      constinteger  set mode = 2
!     %constinteger kill transmit = 8
!     %constinteger kill receive = 4
      !******************************************
      !*  reasons for waiting for a buffer      *
      !******************************************
      constinteger  send name prompt = 1
      constinteger  send pass prompt = 2
      constinteger  put echo on =3, put echo off = 4, send nl = 5
      constinteger  store user name = 6
      constinteger  send disconnect = 7
!     %constinteger send login reply = 8;    ! logon successful
#if b
      constinteger  send busy = 9
#fi
      constinteger  send kill receive = 17

      constinteger  send emas down = 18
      constinteger  send go ahead = 19
      constinteger  send kill transmit = 20
      constinteger  send text marker = 21

      constinteger  last itp reason = 21

      constinteger  low level ip transfer = 22
      constinteger  low level op transfer = 23
      constinteger  get op block = 24
      constinteger  send trig reply = 25;     ! must be odd (output trigger)
      constinteger  send the chop = 26;       ! send an "int y" to 2900
      constinteger  get big op block = 27
      constinteger  kick message stream = 28
      !**************************************************************
      routinespec  to gate(integer  fn, record  (mef) name  mes, c 
        integer  flag)
      routinespec  to 2900(integer  fn, record  (m2900f) name  m2900)
      routinespec  get buffer(integer  reason)
      routinespec  free buffer(record  (mef) name  mes)
      routinespec  from gate
      routinespec  from 2900
      routinespec  from buffer manager(record  (pe) name  p)
      integerfnspec  analyse itp message(record  (mef) name  mes)
      routinespec  retrieve(record  (con desf) name  d)
      routinespec  lose consoles(integer  x)
      routinespec  read from am1
      routinespec  write to am1
      routinespec  kick 2900 message(record  (maf) name  log)
      routinespec  tidy message streams
      routinespec  read message from am1
      routinespec  write message to am1
#if x
       routinespec  mon mes(record  (mef) name  mes)
       routinespec  mon p(record  (pe) name  p)
#fi
#if a
       routinespec  from clock
       routinespec  restart output
       routinespec  get o block
#fi
#if b
       routinespec  from bk
#fi
      !******************************************************
      record  (pe) p

      ownrecord  (tcpf) name  tcp
      owninteger  tcpn

      ownrecord  (con desf) name  d
      ownrecord  (qf) free des; ! holds free descriptors
      ownrecord  (con desf) name  first d;  ! for dumping only

      ownrecord  (con desf) name  d2, d3
      ownrecord  (qf) name  buffer pool
      owninteger  no of buff = 0

#if ~k
      constinteger  tcp limit = 28;  ! increase con statea as well !!!!!!!
#else
      constinteger  tcp limit = 25;  ! increase con statea as well !!!!!!!
#fi
      ownrecord  (tcpf) array  tcpa(0:tcp limit)

#if ~k
      ownbytearray  con index(0:1472)
#else
      ownbytearray  con index(0:857)
#fi

#if ~k
      constinteger  con lim = 118;      ! number of active terminals
#else
      constinteger  con lim = 64;      ! number of active terminals
#fi
      ownrecord  (con desf) array  con desa(-2:con lim)

      constinteger  max ports = 50
      ownbyteintegerarray  porta(0:max ports)
                                        ! cross index from port to tcp
#if k
      constinteger  max tts = 33;      ! ie 0 to 32
#else
      constinteger  max tts = 49;      ! ie 0 to 48
#fi


      owninteger  mon = 0;              ! monitoring flag
      owninteger  lose op = 0;         ! discard output for erte
      constintegername  users == k'100014'; ! no of users in buffer seg

      owninteger  messflag=0;   !w.s.c 9/4/81 tcp connect messages off

      integer  i, n
      ownstring  (63) str

#if x
       ownstring  (1) snil = ""
#fi

#if n
      constinteger  header len = 6
#else
#if r&e
      constinteger  header len = 2
#else
       constinteger  header len = 0
#fi
#fi
      !**********************************************
      !*      initialisation                        *
      !**********************************************

      change out zero = t3 ser

      first d == con desa(0)
      cycle  i = con lim, -1, 0
         push(free des, con desa(i))
      repeat 

      n = 0
      cycle  i = 1, 1, tcp limit
         tcp == tcpa(i)
         tcp_tcpn = i
         tcp_con state ind = n;  n = n+max tts
      repeat 
      d2 == con desa(-2)
      d2_stream = -2
      d3 == con desa(-1)
      d3_stream = -1


#if k
      str=vsn."Kent "
#else
      str=vsn."ERCC "
#fi
#if b
     str=str."(bk)"
#fi
#if x
      str=str."ts "
#else
#if r
      str=str."ring "
#else
      str=str."nsi "
#fi
#fi
      printstring(str.datestring)
     newline

      map hwr(0);                      ! map am1 to segment 0
      i = map virt(buffer manager, 5, 4);   ! map buff man stack to seg 4
      i = map virt(buffer manager, 6, 5);   ! and second seg
      users = 0
      con desa(i)_stream = i for  i = 0, 1, con lim

      p_c = 2;                  ! param for 'here i am'
      to 2900(here i am, null)
      p_c = 3;                        ! and claim stream 3
      to 2900(here i am, null)

      tcp == tcpa(0);                 ! dummy for below
#if ~x
      to gate(enable facility, null, 18)
#else
       p_ser = gate ser; p_reply = own id
       p_fn = enable facility; p_a2 = 0; p_facility = "ITP"
       pon(p)
#fi
#if a
      alarm(100)
#fi
      !**********************************************
      !*           main loop                        *
      !**********************************************
      cycle 
         p_ser = 0;  poff(p)

#if a
         if  p_reply=0 then  from clock and  continue 
#else
         if  int # 0 start 
         if  'M' <= int <= 'P' start 
            mon = int-'O'
         finish 
         if  int='A' then  messflag=1;   !turn messages on
         if  int='B' then  messflag=0;   !turn off
         if  int = '?' start ;   ! $$ mon
            write(no of buff, 4); newline
            printstring("term  qu  mq held  no held
")
            cycle  i = 1, 1, tcp limit
               tcp == tcpa(i)
               if  tcp_state = connected start 
                   write(tcp_term, 3)
                  write(tcp_size, 3);  write(tcp_max, 3)
                  write(tcp_held, 3); write(tcp_h no, 5)
                   newline
                   tcp_max = 0
               finish 
            repeat 
         finish 
         int = 0
         finish 
#fi

         if  p_reply = link handler start 
            from 2900
         finish  else  if  p_reply = gate ser start 
            from gate
#if b
         finish  else  if  p_reply = host bk ser start 
             from bk
#fi
         finish  else  if  p_reply = buffer manager then  from buffer manager(p)
      repeat 

      !*************************************************
      !*           routines to do the work             *
      !*************************************************

#if a
     routine  restart output
     integer  x, q
            x = tcp_h ind
            cycle 
               x = x+1
               if  x >= max tts then  x = 0
               q = con index(x+tcp_con state ind)
               unless  q = 0 start ;  ! console active
                  d == con desa(q)
                  if  d_hold f # 0 start ; ! and held
                     d_hold f = 0; tcp_held = tcp_held-1
                     get o block
#if ~k ! x
                     if  tcp_size > 0 then  c 
                        tcp_h ind = x and  ->got it
                           !! if q is still non-zero, release only 1
#else
                      tcp_h ind=x; ->got it
#fi
                  finish 
               finish 
            repeat  until  x = tcp_h ind
            tcp_held = 0;               ! didn't find any!
   got it:
     end 

     routine  from clock
     integer  i
         if  int # 0 start 
         if  'M' <= int <= 'P' start 
            mon = int-'O'
         finish 
         if  int='A' then  messflag=1;      !turn messages on
         if  int='B' then  messflag=0;      !turn off
         if  int = '?' start ;   ! $$ mon
            write(no of buff, 4)
            newline
            printstring("term  qu  mq held  no held en_in")
            newline
            cycle  i = 1, 1, tcp limit
               tcp == tcpa(i)
               if  tcp_state = connected start 
#if k
                  printstring(tcp_name)
#else
                   write(tcp_term, 3)
#fi
                  write(tcp_size, 3)
                  write(tcp_max, 2)
                  write(tcp_held, 3)
                  write(tcp_h no, 2)
#if a
                   write(tcp_en in,1);  !input blocked if #0
#fi
                   newline
                   tcp_max = 0
               finish 
            repeat 
         finish 
            int = 0
         finish 
         for  i=1,1,tcp limit cycle 
            tcp==tcpa(i)
            if  tcp_state#connected then  continue 
            if  tcp_en in > 0 start 
               tcp_en in count=tcp_en in count + 1
               if  tcp_en in count > 10 start 
                  printstring("itps: ")
#if k
                  printstring(tcp_name)
#else
                  printstring("TCP")
                  write(tcp_term, 3)
#fi
                  printstring(" appears to be stuck, should it be reloaded?")
                  newline
                  tcp_en in count=0
               finish 
            else 
               if  tcp_en in count>0 then  tcp_en in count=tcp_en in count - 1
            finish 
            if  tcp_held#0 and  tcp_size=0 andc 
                no of small>=critical start 
                restart output
                printstring("Output restarted ")
#if k
                printstring(tcp_name)
#fi
                newline
                exit 
             finish 
          repeat 
          alarm(100)
   
     end 
#fi

#if k
      string (8)fn  itos(integer  i)
      bytearray  c(0:7)
      string (8)s
      integer  k
         k=0
         if  i<0 start 
            c(0)='-';  k=1;  i=-i
         finish 
         cycle 
            c(k)=i-i//10*10+'0';  i=i//10
            k=k+1
         repeatuntil  i=0
         length(s)=k
         for  i=1,1,k cycle 
            charno(s,i)=c(k-i)
         repeat 
         result =s
       end 
#fi

      routine  crunch
         integer  i
         cycle  i = 1, 1, 15
            printstring("itps: Bad buffer ***** dump fep ********
")
         repeat 
         *=k'104001';                   ! emt wait
      end 

      routine  to gate(integer  fn, record  (mef) name  mes, c 
         integer  flag)
         if  fn = put output start ;        ! queue these as necessary
            if  tcp_state # connected start ; ! throw away
                free buffer(mes);  return 
            finish 

            if  addr(mes)&k'140000' = k'140000' then  crunch; ! had it
#if ~k ! x
            if  tcp_ostate # idle start 
               push(tcp_outq, mes)
               tcp_size = tcp_size+1
               tcp_max = tcp_size if  tcp_size>tcp_max
               return 
            finish 
            tcp_ostate = busy
#else
            tcp_size = tcp_size + 1
            tcp_max = tcp_size if  tcp_size>tcp_max
#fi

!            %if mon # 0 %start
!               select output(1)
!               printstring("io ");!  mon mes(mes)
!            %finish
         finish 

         p_ser = gate ser; p_reply = own id
         p_fn = fn; p_gate port = tcp_port; p_mes == mes
#if ~x
         p_c2 = flag
#else
          p_a2 = flag
          p_task port = tcp_tcpn
          if  mon # 0 start 
             select output(1);  spaces(5)
             printstring("itp: to gate:");       mon p(p)
             select output(0)
          finish 
#fi
         pon(p)
      end 

      routine  to 2900(integer  fn, record  (m2900f) name  m2900)
         p_ser = link handler; p_reply = own id
         p_fn = fn; p_mes == m2900
         pon(p)
      end 

      routine  get buffer(integer  reason)
         record  (pe) p
         integer  type
      !*******************************************************
      !*    hold a pool, so can call buffer here immedialtely*
      !*         otherwise hold the activity until it arrives*
      !*******************************************************

         if  reason = get big op block then  type=0 else  type=1
         p_c2 = reason
#if ~x
         p_gate port = d_stream
#else
          p_a2 = d_stream
#fi
         if  buffer pool == null or  type=0 start ;  ! have to ask for it
            p_ser = buffer manager; p_reply = own id
            p_fn = request buffer
            p_c1 = type;                     ! either size
            pon(p)
         else 
            p_mes == buffer pool;  buffer pool == p_mes_link
            p_mes_link == null
            no of buff = noof buff-1;  from buffer manager(p)
         finish 
      end 

      routine  free buffer(record  (mef) name  mes)
         record  (pe) p

         if  addr(mes)&k'140000' = k'140000' then  crunch

         if  mes_type=0 or  no of buff>10 or  no of small < 15 start 
            p_ser = buffer manager; p_reply = own id
            !! queue it if it is a short buffer
            p_fn = release buffer; p_mes == mes
            pon(p)
         else 
            !! short buffer, so queue it
            mes_link == buffer pool; buffer pool == mes
            no of buff = no of buff+1
         finish 
      end 


#if x
       string  (127) fn  unpack(record  (mef) name  mes, integer  no)
          integer  i, l
          unless  mes == null or  mes_len<=0 or  no<=0 start 
             l = 0
             while  no>1 cycle 
                l=l+mes_params(l)+1
                no = no-1
             repeat 
             result  = string(addr(mes_params(l)))
          finish  else  result  = ""
       end 

       routine  pack(record (mef) name  mes, string  (*) name  s)
          string(addr(mes_params(mes_len))) = s
          mes_len = mes_len+length(s)+1
       end 
#fi

      routine  get o block

         !! this routine determines whether it is worth asking for
         !! a big buffer to put itp output in, otherwise gets small

         !! nb: 1st transfer is always a small buffer (not done here)

         integer  x
         x = d_o lim-d_o pos
         if  x<0 then  x=x+d_out lim
         if  x>small block max and  no of big>15 then  c 
           get buffer(get big op block) else  c 
           get buffer(get op block)
      end 

#if b

routine  to bk(integer  stream, fn);    !to booking server
!---------------------------------

    p_ser=host bk ser;   p_reply=own id
    p_fn=fn;  p_c=stream
    pon(p)

end 

routine  from bk
!--------------        message from booking server
!                       either reply to can i logon or a throw off
record  (maf) name  m
integer  index

   m==p_mes
   index=p_c;  !stream number
   unless  0<=index<=con lim start 
      printstring("itps:illegal stream no. from HY")
      write(index, 2)
      newline
      free buffer(p_mes)
      return 
   finish 
   d==con desa(index)
   tcp==tcpa(d_tcp)
   index=index*2 + fixed
   if  p_fn=logon reply start 
#if x
      if  p_a2=bkaccept start ;   !send logon request to the host
#else
      if  p_gate port=bkaccept start 
#fi
         kick 2900 message(m);    !NB corrupts d
         p_c=index
         to 2900(here i am, null);!tell am1 handler
         p_c=index+1
         to 2900(here i am, null)
      else ;                      !logon request rejected
         free buffer(p_mes)
         if  d_cnsl=255 start 
            retrieve(d)
         else 
            get buffer(send busy)
            d_state=logging off
         finish 
      finish 
   else ;          !force a logoff
      m_a(1)=6;    !code for force off
      m_a(2)=0
      m_a(3)=index
      m_a(0)=m_a(4)+4
      kick 2900 message(m);       !NB corrupts d
   finish 

end 
#fi
      routine  from gate
         record  (mef) name  mes
         record  (tcpf) name  targ
         integer  fn, flag, type, x, q
#if k&~x
         bytearrayname  tsparams
         integer  i, l, k
#fi
#if ~x
         switch  fns(incoming call:call aborted)
#else
          switch  fns(connect:Datagram Reply)
          string  (63) calling
          string  (9) qual
#fi

         fn = p_fn
#if ~x
         tcpn = porta(p_gate port)
#else
          tcpn = p_task port
#fi
         tcp == tcpa(tcpn)
         mes == p_mes
#if x
          if  mon # 0 start 
             select output(1); spaces(5)
             printstring("itp: from gate:")
             mon p(p)
             select output(0)
          finish 
#fi

         ->fns(fn)

#if ~x
    fns(incoming call):
#else
     fns(Connect):
#fi
         tcp == null
         cycle  tcpn = tcp limit, -1, 1
            targ == tcpa(tcpn)
               if  targ_state = not allocated then  tcp == targ
         repeat 
         if  tcp == null start 
                                        ! 2900 down or full up
            tcp == tcpa(0)
            tcp_port = p_gate port;          ! for 'to gate' call only
#if ~x
            flag = reject
            to gate(call reply, null, flag)
#else
             to gate(Disconnect, null, 17)
             free buffer(mes)
#fi
            return 
         finish 

#if ~x
         tcp_term = p_c1
#fi
        tcp_state = connected;  tcp_ostate = idle
#if ~x
#if k
         tcp_node=0
#else
         tcp_node = p_mes_lev3_reserved(4);   ! really mes_nsl_sn - but hi a compiler fault!
#fi
         porta(p_gate port) = tcp_tcpn;   ! fill in port no - tcp no index
#fi
         tcp_port = p_gate port
#if ~x
#if n
         flag = p_c1;        ! pick upp fl & rl
         x = flag&x'70';         ! pick up fl
         if  x>x'20' then  x = x'20'; ! limit to  2
         flag = (flag&x'f')!x
#else
         flag = 1
#fi
#else

          tcp_node = 0;       ! ??????
          calling = unpack(mes, 2)
#if k
          tcp_name<-calling
#fi
          qual = unpack(mes, 3)
#fi
#if k&~x
          tsparams==p_mes_lev3_itp_a
          if  p_mes_len>8 and  tsparams(5)=128 and  tsparams(6)=16 andc 
             tsparams(8)>=132 start 
                l=tsparams(8)&63;   if  l>8 then  l=8
                length(tcp_name)=l
                k=-1;   !to control byte swapping
                for  i=1,1,l cycle 
                   charno(tcp_name,i)=tsparams(7+i+k)
                   k=-k
                repeat 
             else 
                tcp_name="T".itos(tcp_term)
             finish 
#fi

         if  messflag=1 start 
#if ~x
#if k
            printstring("       itp: "); printstring(tcp_name)
#else
            printstring("       itp: t")
            write(p_c2, 1)
#fi
#else
             printstring("       itp: ")
             printstring(calling)
#fi
           printstring(" connected
")
         finish 

         tcp_max = 0;          ! for monitoring
         tcp_size = 0; tcp_held = 0; tcp_h no = 0
#if ~x
         to gate(call reply, null, flag)
#else

          mes_len = 0
          pack(mes, snil)
          pack(mes, qual)
          pack(mes, snil)
          to gate(accept call, mes, 0)
#fi
         return 

#if x
     fns(expedited data):                     !     int message (i hope)
#fi

fns(input here):


         mes_len = mes_len - header len
         if  mes_len <= 3 start 
            free buffer(mes)
#if x
!see if data is pushed, if so treat as close request
            if  p_a2#0 start 
               to gate(disconnect, null, 0)
               tcp_state = tcp disc
            finish 
#fi
            return 
         finish 

#if a
         if  tcp_size < 5 then  to gate(enable input, null, 1) elsec 
              tcp_en in = tcp_en in + 1
#else
         to gate(enable input, null, 1)
#fi

#if x
          if  mon < 0 start 
             select output(1)
             printstring("ii ");       mon mes(mes)
          finish 
#fi

#if ~k
         mes_lev3_reserved(0) = 0;          ! missing gah count
#fi
         flag = analyse itp message(mes)
         if  flag < 0 then  free buffer(mes)
                                            ! flag > 0  - going to 2900
                                            ! flag = 0  - used internally
                                            ! flag < 0  - may be freed
         return 

#if ~x
    fns(output transmitted):
#else
     fns(enable output):
#fi
#if ~k ! x
         tcp_ostate = idle
         unless  tcp_outq_e == null start 
            tcp_size = tcp_size-1
            to gate(put output, pop(tcp_outq), 0)
         finish 
#else
         tcp_size = tcp_size -1
#fi
         if  tcp_held # 0 and  tcp_size<5 start 
               !! consoles are held & q is now redduced
#if a
            restart output
#else
            x = tcp_h ind
            cycle 
               x = x+1
               if  x >= max tts then  x = 0
               q = con index(x+tcp_con state ind)
               unless  q = 0 start ;  ! console active
                  d == con desa(q)
                  if  d_hold f # 0 start ; ! and held
                     d_hold f = 0; tcp_held = tcp_held-1
                     get o block
                     if  tcp_size > 0 then  c 
                       tcp_h ind = x and  -> got it
                            !! if q is still non-zero, release only 1
                  finish 
               finish 
            repeat  until  x = tcp_h ind
            tcp_held = 0;               ! didn't find any!
got it:
#fi

         finish 
#if a
         if  tcp_en in > 0 and  tcp_size < 5 start 
            to gate(enable input, null, 1)
            tcp_en in = tcp_en in - 1
         finish 
#fi
         return 

#if ~x
    fns(call closed):
#if r
         flag = 0
         type = close call
         -> kill it
#else
         return ;                          ! handled in 'input recd'
#fi
#fi

#if ~x
    fns(call aborted):                      ! either way, all is lost
#else
     fns(Disconnect):                    ! Call has been cleared
         unless  p_mes==null then  free buffer(p_mes)
#fi
#if ~x
         flag = 1
         type = abort call;             ! nb: cpmatibility with x25 vsn
kill it:
#else
          flag = p_a2;                   ! pickup reason for close
#fi

         if  messflag=1 start 
#if k
            printstring("       itp:"); printstring(tcp_name)
#else
            printstring("       t");  write(tcp_term, 1)
#fi
            printstring(" connection ")
#if x
            if  (tcp_state = tcp disc and  flag = 1) or  flag = 0 start 
               printstring("closed")
            finishelsestart 
               printstring("aborted");  write(flag, 1)
            finish 
#else
            if  flag # 0 then  printstring("aborted") else  c 
              printstring("closed")
#fi
            write(tcp_max, 1); newline
         finish 
         lose consoles(-1)
#if ~x
         to gate(type, null, 0)
#else
          if  tcp_state#tcp disc then  to gate(disconnect, null, 1)
#fi
         tcp_state = not allocated
         x = 0
         while  not  tcp_outq_e == null cycle 
            free buffer(pop(tcp_outq))
            x = x+1
            if  x&7 = 7 then  set prio(1)
               !! force a reschedule, to avoid overload
         repeat ;                     ! flush any queued items

      end 


      integerfn  analyse itp message(record  (mef) name  mes)

         record  (itpf) name  itp, itp2
         integer  cnsl, index, stream, len, q, x
         record  (maf) name  m
         record  (m2900if) name  mi
         string  (15) int mes

         switch  console state(idle:logging off 2)

         itp == mes_lev3_itp
         cnsl = itp_cnsl
         if  cnsl >= max tts  start 
            printstring("itps: cnsl no too high, tcp,cnsl:")
            write(tcp_term, 1); write(cnsl, 1)
            newline
            -> get rid of it
        finish 

         index = cnsl+tcp_con state ind
         q = con index(index)
         unless  q = 0 start 
            d == con desa(q)
            if  cnsl#d_cnsl or  d_tcp#tcp_tcpn start 
               printstring("itps: console mismatch (warning)
")
               -> get rid of it
            finish 

            if  itp_hdb1&i disconnect # 0 start 
               !! console ctrl+d
               lose consoles(cnsl)
               -> get rid of it
            finish 

            if  itp_hdb1&go ahead# 0 start ; ! 'simple' goahead
               d_out go = d_out go+1
               if  d_out go > 4 then  d_out go = 4
               if  d_out go = 1 and  d_ostate &out p # 0 start 
                  if  tcp_size >= 4 or  no of small < critical start 
                     d_hold f = 1; tcp_held = tcp_held+1; tcp_h no=tcp_h no+1
                  finish  else  get o block
               finish 
            finish 

            ->console state(d_state)

         finish 


console state(not allocated):           ! eg no descriptor
         if  itp_hdb1&hello # 0 start ; ! sent hello
            d == pop(free des)
            if  d == null then  -> get rid of it
            stream = d_stream;       ! hold the stream
            con index(index) = stream
            d = 0;                   ! zero the record
            d_stream = stream
            d_tcp = tcpn;  d_cnsl = cnsl
            d_out go = 1 if  itp_hdb1&go ahead # 0

            d_state = name sent;        ! if down, goes to logging off whe sent
            users = users+1

            if  host state = down start 
               get buffer(send emas down)
               result  = -1
            finish 
            get buffer(send name prompt)
            d_hold == mes
            get buffer(store user name)
            result  = 2;                ! buffer retained
         finish 
         result  = -1;                  ! no further

console state(name sent):               ! user name arrived ?

         if  itp_hdb1&control = 0 start ;    ! is a text message
            d_state = pass sent
            get buffer(put echo off);   ! switch echo off
            get buffer(send pass prompt);    ! send pass:
            if  length(itp_s) > 2 then  length(itp_s) = length(itp_s)-2
            if  length(itp_s) > 20 then  length(itp_s) = 20
            m == d_hold;                ! pickup buffer with 'address'
            string(addr(m_a(m_mlen))) = itp_s
            m_mlen = m_mlen+length(itp_s)+1
         finish 
         result  = -1;                  ! de-alloctae block

console state(pass sent):               ! password arrived ??

         if  itp_hdb1&control = 0 start ;   ! ia a text message
            d_out go = d_out go-1
            get buffer(send nl);        ! send out a newline
            get buffer(put echo on);      ! put echo back on
            m == d_hold
            !! check that it has switched buffers??
            if  length(itp_s) > 2 then  length(itp_s) = length(itp_s)-2;   ! delete the cr/lf

#if k
!convert password to upper case
            for  x=1, 1, length(itp_s) cycle 
               if  'a'<=charno(itp_s, x)<='z' thenc 
                  charno(itp_s, x)=charno(itp_s, x)-'a'+'A'
            repeat 
#fi
            x = m_mlen
            if  x+length(itp_s) > small block max then  c 
              length(itp_s) = small block max-x
            string(addr(m_a(x))) = itp_s; ! put in password
            x = x+length(itp_s)
            m_a(0) = x

            d_hold == null
            d_seq bits = x'c0'
            d_state = logging on
#if b
            p_mes==m
            to bk(d_stream, can i logon)
#else

            index = d_stream<<1+fixed
            kick 2900 message(m);       ! nb: disturbs 'd'
            p_c = index;             ! param for 'here i am'
            to 2900(here i am, null)
            p_c = index+1;           ! param for 'here i am'
            to 2900(here i am, null)
#fi

         finish 
         result  = -1

console state(logging on):              ! go ahead only?
console state(logged on):               ! still no input
      result  = -1

console state(input enabled):           ! input messages and ints

         !! check for a text message

         if  itp_hdb1&control = 0 start ; ! text
#if k
            d_gahs sent=d_gahs sent-1
            if  d_gahs sent<0 start 
               printstring("itps:too much input!!");
               write(tcp_term,3); write(d_cnsl,3); newline
               d_gahs sent=0
            finish 
#fi
            if  not  d_in mes == null start 
               d_seq bits = d_seq bits+seq inc
               itp2 == d_in mes_lev3_itp
#if ~k
               d_in mes_lev3_reserved(0) = d_in mes_lev3_reserved(0)+1; ! missing gah count
#fi
               unless  length(itp_s)+length(itp2_s)>240 then  c 
                  itp2_s = itp2_s.itp_s
               result  = -1;           ! chuck the buffer
            finish 
            get buffer(low level ip transfer);                         ! signal to 2900 input here
            d_in mes == mes
         mes_lev3_reserved(1) = 0;          ! pos in block flag = 0
            result  = 2
         finish 

         !!         check for an "int" messgae

         if  itp_hdb2&intm # 0 start ;       ! int message
            int mes = itp_s;                 ! copy it out of the way

            len = length(int mes);       ! check for cr, nl  & nl
            if  charno(int mes, len-1) = 13 then  len = len-2
            if  charno(int mes, len) = nl then  len = len-1
            len = 15 if  len > 15
            result  = -1 if  len <= 0;  ! invalid int
            length(int mes) = len

            mi == mes;                        ! re-use 'mes'
            mi_stream = (d_stream<<1)+fixed;  mi_sub ident = 0
            mi_p2a = -1;  mi_p2b = -1;  ! set up params
            mi_int = int mes;                 ! copy string accross
            to 2900(send data, mi);        ! send to am1h
            result  = 2;                     ! don't deallocate buffer
        finish 

         if  itp_hdb2&set mode # 0 start ;    ! setmode in
            str = itp_s;                      ! copy to global string
            m == mes;                         ! change to 'to 2900' type
            m_a(1) = 2;                       ! type = set mode
            m_a(2) = 0;                       ! top half of stream
            m_a(3) = d_stream<<1+fixed;       ! rest of stream
            string(addr(m_a(4))) = str;       ! copy setmode back in
            m_a(0) = length(str)+4;           ! put in total length
            kick 2900 message(m);             ! put in q for 2900
            result  = 2;                      ! dont free buffer
         finish 
         result  = -1


console state(logging off):             ! message is out, just disconnect
         d_state = logging off 2
         get buffer(send disconnect)
         result  = -1


get rid of it:
console state(logging off 2):            ! ignore
         result  = -1
      end 



      routine  free transient
         if  not  d_in mes == null then  free buffer(d_in mes) and  c 
           d_in mes == null
         if  not  d_hold == null start 
            free buffer(d_hold); d_hold == null
        finish 
      end 

      !! r o u t i n e    from 2900 

      !!  all messages from the 2900 come to this routine

      routine  from 2900
         record  (m2900f) name  m2900
         record  (m2900bf) name  m2900b
         record  (m2900cf) name  m2900c
         record  (mef) name  mes

         integer  stream, sub ident, state, trig, l reply, mode, i
         integer  type, p2b, pf
         switch  link fns(interf addr:mainframe down)

         m2900 == p_mes;  m2900b == m2900
         if  p_fn = message  start 
            stream = m2900_stream;               ! get first stream no
         else 
            if  p_fn > message then  ->link fns(p_fn)
            stream = p_c
         finish 
         d == con desa((stream-fixed)>>1)
          tcp == tcpa(d_tcp)
         -> link fns(p_fn)


link fns(interf addr):               ! interface addr from eam5
         l == record(addr(p_mes)&k'17777');  ! force to seg 0
         return 


link fns(do output):         ! -> 11/34
         if  stream = 3 then  read message from am1 else  c 
           read from am1
!         ->d mon
         return 

link fns(do input):      ! -> 2900
         if  stream = 2 then  write message to am1 else  c 
           write to am1
!d mon:   %if mon #0 %start
!            select output(1);!  printsymbol('t')
!            write(p_fn, 1);!  write(stream, 1);! newline;!  select output(0)
!         %finish
         return 

link fns(mainframe up):
         printstring("emas-2900 up
")
         -> tidy

link fns(mainframe down):
         printstring("Emas Down
")
tidy:    tidy message streams
         cycle  i = 0, 1, con lim
            d == con desa(i)
            if  d_state # not allocated and  d_cnsl=255 then  c 
              retrieve(d) else  start 
               if  not allocated < d_state < logging off start 
                  free transient
                  get buffer(send emas down)
                  d_state = logging off
               finish 
            finish 
            if  i&3 = 3 then  set prio(1);  ! force re-schedule
         repeat 
         host state = down
         users = -1
         return 


link fns(message):
         if  stream = 2 then  d == d2
         if  stream = 3 then  d == d3
         type = 0
         sub ident = m2900_sub ident
         state = m2900b_b(1);  mode = m2900b_b(0)
            if  mon < 0 start 
               select output(1)
               printstring("mess:")
               write(stream, 1); write(sub ident, 1); write(state, 1)
               write(m2900_p2b, 1); write(m2900_p3b, 1)
               newline
               select output(0)
            finish 


         if  sub ident # 0 start ;      ! low level
            if  stream <= 3 start 
               if  state = connecting start 
                  !! initial logon stream connected
                  host state = up
                  printstring("logon stream connected
")
                  users = 0
               else 
                  if  state = enabling start 
                     d_o state = enabld
                     d_state = logged on;     ! not quite right, but?
                     d_out lim = m2900_p2b; d_o pos = 0; d_o lim = 0; d_o posx=0
                     printstring("logon stream enabled
")
                  finish 

                  if  state = disconnecting start 
                     host state = down
                     printstring("logon stream disconnected
")
                  tidy message streams
                  finish 
               finish 
            else 

               if  d_state = not allocated start 
                  if  stream&1=0 start ; ! monitor input str only
                     printstring("itps: not allocated problem")
                     write(state, 1); newline
                  finish 
                  -> send reply
               finish 

               if  state = enabling start ;   ! 1st intersting condition
                  if  stream&1 = 0 start 
                     d_state = input enabled
                     d_seq bits = x'c0'
                     if  d_cnsl = 255 start ;   ! gone away
                         type = 1
                     else 
                        d_in lim = m2900_p2b
                        d_i pos = m2900_p3b
#if ~k
                        get buffer(send go ahead);  get buffer(send go ahead)
#else
                        d_gahs wanted=3
                        d_gahs sent=0
#fi
                        get buffer(send go ahead)
                     finish 
                  else 
                     if  d_out lim # 0 start 
                        if  d_abortf & output aborted #0  start 
                           !! an 'aborting' has been done
                           get buffer(send text marker)
                           d_out go = d_out go-1
                        finish 
                     finish 

                     d_out lim = m2900_p2b;  d_o state = enabld
                     d_o pos = m2900_p3b;  d_o lim = 0;  d_p lim = 0
                     d_mode = mode>>4; ! 0-iso,x'20'-bin,x'30'-cont
                  finish 
               finish  else  if  state = disconnecting start 
                  if  stream&1 = 0 then  d_in lim = 0 else  start 
                     d_out lim = 0
                     d_o state = idle
                  finish 
                  if  d_in lim = 0 and  d_out lim = 0 start 
                                        ! both disconnected
                     d_state = logging off
                     get buffer(send disconnect)
                  finish 

               finish  else  if  state = aborting or  state = suspending start 
                  if  stream&1 # 0 start ;     ! output side
                      d_o state = idle;       ! stop transfers
                     if  state = aborting start 
                        d_abortf = d_abortf ! output aborted
                        get buffer(send kill transmit)
                     else 
                        d_abortf = d_abortf & (¬output aborted)
                     finish 
                     if  not  d_hold == null then  c 
                       free buffer(d_hold) and  d_hold == null
                  else ;       !input has been aborted
                     if  state = aborting start 
                        d_abortf = d_abortf ! input aborted
                        get buffer(send kill receive)
                     else 
                        d_abortf = d_abortf & (¬input aborted)
                     finish 
                  finish 
               finish 
            finish 
            m2900_p2a = 0;  m2900_p2b = 0
send reply:
            to 2900(low level control, m2900)
            if  type # 0 then  get buffer(send the chop)
            return 
         finish 

         !*********************************
         !* high level message
         !********************************
         if  stream&1 = 0 and  stream > 2 start ;        ! input high level
            trig = m2900_p3b
            if  d_i pos = trig start 
               d_p lim = m2900_p2b
               i = d_o state
               d_o state = i!pmt p
               d_pmt n = d_seq bits!terminated!prompt!seq no valid
                                         ! hold for use later
               if  i = enabld start 
                  d_hold == m2900;          ! retain buffer
                  get buffer(low level op transfer)
                  return 
               finish 
            finish 
            free buffer(m2900);      ! past that position already
         else 
            !************************
            !*  output  stream      *
            !************************
            if  stream = 3 start 


               !! update of pointer on message stream
               p2b = m2900_p2b
               free buffer(m2900)
if  mon < 0 start 
write(d_olim, 2); write(d_opos, 2); write(p2b, 2); write(d_out lim, 2); newline
finish 
               get buffer(get op block) if  d_o lim = d_o pos
               d_o lim = p2b
            else 

               !! request output message
               ! %integer output pos, trig pos

               d_o lim = m2900_p2b
               d_o trig = m2900_p3b
               m2900_p3a = k'050505';    ! diagnostic purposes

               !! check whether immediate trig reply is needed

               if  d_o trig >= 0 start ;    ! maybe
                  get buffer(send trig reply) if  d_opos = d_olim or  c 
                    (d_opos<d_olim and  not  d_opos<d_otrig<=d_olim) c 
                     or  c 
                    (d_opos>d_olim and  d_olim<=d_otrig<=d_opos)
                  finish 

                d_o state = d_o state&(¬pmt p);    ! discard prompt
                if  d_o state&out p = 0 and  d_opos # d_olim start 
                   d_ostate = d_ostate!outp
                  if  d_out go > 0 start ;  ! allowed to send
                     if  not  d_hold == null start 
                        free buffer(m2900)
                     else 
                        d_hold == m2900
                     finish 

                     if  tcp_size>=4 or  no of small < critical start 
                        d_hold f = 1; tcp_held = tcp_held+1
                        tcp_h no  = tcp_h no+1
                        free buffer(d_hold); d_hold == null
                     else 
                        get buffer(low level op transfer)
                     finish 
                     return 
                  finish 
               finish 
               free buffer(m2900)
            finish 
         finish 
      end 

      routine  fill(record  (mef) name  mes, integer  no)
         integer  n, pt, max
#if k
         integer  p, l
#fi

#if ~b
         constbyteintegerarray  pts(1:last itp reason) =
           1, 10, 19, 25, 31, 37, 38, 42, 42, 42(7), 75,
         42, 61, 65, 69
#else
         constbyteintegerarray  pts(1:last itp reason) =
           1, 10, 19, 25, 31, 37, 38, 42, 75, 42(7), 118,
         42, 61, 65, 69
#fi
           !! pt to itp mess

#if ~b
         ownbyteintegerarray  itp message(1:78) =
#else
         ownbyteintegerarray  itp message(1:121) =
#fi
         8, 2, k'146', 5, 'U', 's', 'e', 'r', ':',;  ! name prompt
         8, 0, k'246', 5, 'P', 'a', 's', 's', ':',;  ! pass prompt
         5, 1, 2, 2, 1, 1,;                           ! echo on
         5, 3, 2, 2, 1, 0,;                           ! echo off+go ahead
         5, 0, 2, 2, 13, nl,;                        ! nl
         0,;                                         ! not used
         3, 5, 0, 0,;                                ! i disconnect
         18, 0, 2, 15, 13, nl, '*', '*', '2', '9', '0', '0', ' ',
            'D', 'o', 'w', 'n', 13, nl,;             ! emas down
          3, 3, 0, 0,;                                ! go ahead
          3, 1, 8, 0,;                                ! kill transmit
          5, 0, 10, 2, 13, nl,;                      ! nl+text marker
#if ~b
          3, 1, 4, 0;                                 !kill receive
#else
          42,0,2,39,13,nl,'*','*','*','S','o','r','r','y',' ',
          't','h','e','r','e',' ','a','r','e',' ','n','o',' ',
          'f','r','e','e',' ','c','o','n','s','o','l','e','s',13,nl,
           3, 1, 4, 0;                                !kill receive
#fi

         pt = pts(no)
         string(addr(mes_lev3_itp_aa(0))) = string(addr(itp message(pt)))
#if k
         if  no = send go ahead start 
            l = itp message(pt);    !length of gah
            p = l+1
           d_gahs sent=d_gahs sent+1;     !always send one, loop below for any more
            while  d_gahs sent<d_gahs wanted cycle 
               if  p>100 start 
                  printstring("itps:too many gahs!!")
                  write(tcp_term,3); write(d_cnsl,3)
                  write(d_gahs sent,3); write(d_gahs wanted,3); newline
                  exit 
               finish 
               string(addr(mes_lev3_itp_aa(p))) = string(addr(itp message(pt)))
               mes_lev3_itp_aa(p) = d_cnsl;      !overwrite str len with cnsl
               p = p + l+1;     !depends on length of gah
               d_gahs sent=d_gahs sent+1
            repeat 
            mes_len = p+header len
         else 
            mes_len = mes_lev3_itp_aa(0)+header len+1;          ! nsi+cnsl no
         finish 
#else
         mes_len = mes_lev3_itp_aa(0)+header len+1;          ! nsi+cnsl no
#fi
      end 


      !! r o u t i n e  move user name  (from big to small buffer)

      routine  move user name(record  (maf) name  logr)
         record  (mef) name  mes
         string  (24) add
         string  (24) name  s
         integer  la

         !  N B
         !      Total length of addr, name and password must not exceed
         !      small block max
         !      Password is truncated if this is so

         if  d_state # name sent start 
           printstring("itp:mun fails")
           write(d_state, 1); newline
           free buffer(logr);  return 
        finish 
         mes == d_hold
         logr_a(1) = 1
         logr_a(2) = 0
         logr_a(3) = d_stream<<1+fixed

#if ~k
         ! until tcp passes address
         length(add) = 3
         charno(add, 1) = tcpa(d_tcp)_node
         charno(add, 2) = tcpa(d_tcp)_term
         charno(add, 3) = d_cnsl
         string(addr(logr_a(4))) = add.mes_lev3_itp_s
#else
         add=tcp_name."::".itos(d_cnsl);    !field for terminal speed blank
         s==mes_lev3_itp_s
         while  length(s)>0 and  0<=charno(s,length(s))<=31 cycle 
            length(s)=length(s)-1
         repeat 
         string(addr(logr_a(4)))= add.":".s
#fi
         logr_mlen = logr_a(4) + 4 + 1
         free buffer(mes)
         d_hold == logr
      end 


      !! r o u t i n e   from buffer manager

      !! all requests for buffers come back through here

      routine  from buffer manager(record  (pe) name  p)
         integer  reason, type
         record  (m2900f) name  m2900
          record  (mef) name  mes
         record  (m2900if) name  mi
         conststring  (1) the chop = "Y"

         reason = p_c2;                 ! get reason for calling
#if ~x
         n = p_gate port;                    ! byte quantity !
#else
          n = p_a2
#fi
         if  n >= 254 then  n = n-256
         d == con desa(n);  ! get console descriptor
         tcp == tcpa(d_tcp);!in case it's needed
         if  mon < 0 start 
            select output(1); printstring("from buff:")
            write(p_gate port, 1); write(n, 1); write(reason, 1)
            write(d_stream, 1); write(d_state, 1)
            newline; select output(0)
         finish 
         if  d_state = not allocated then  -> free
         if  reason = store user name then  move user name(p_mes) andc 
           return 
         if  reason <= last itp reason start 
            if  d_cnsl # 255 start ;   ! cnsl = 255 - disconnected
               fill(p_mes, reason);    ! insert the message
               p_mes_lev3_itp_cnsl = d_cnsl
               to gate(put output, p_mes, 0)
               if  reason = send emas down then  d_state = logging off
            else 
free:          free buffer(p_mes)
            finish 

            if  reason = send disconnect start 
               retrieve(d)
           finish 
         else 

            if  reason=get op block or  reason=get big op block start 
               if  d_o state = idle then  -> free; ! kill o/p done
               unless  d_hold==null then  free buffer(d_hold)
               d_hold == p_mes
               get buffer(low level op transfer)
               return 
            finish 

            !! message to 2900 reason
            m2900 == p_mes
            m2900_stream = d_stream<<1+fixed+reason&1
            m2900_sub ident = 10

            if  d_stream < 0 then  m2900_stream = 4+d_stream

            if  reason = low level op transfer start 
               mes == d_hold
               if  mes == null then  -> free
                             ! kill op done, so ignore tran request
               length(mes_lev3_itp_s) = 1
               m2900_p2a = k'400';        ! = swab(1)
               m2900_p2b = swab(d_o pos)
            else 
               m2900_p2b = 0;  m2900_p2a = 0
            finish 

            type = low level control

            if  reason = send trig reply start 
               m2900_sub ident = 0
               m2900_p5a = 0;  m2900_p5b = swab(d_opos)
               type = send data
               d_o trig = -1
            finish 
            if  reason = send the chop start 
               mi == m2900;  mi_sub ident = 0;  type = send data
               mi_p2a = -1;  mi_p2b = -1
               mi_int = the chop
            finish 

            if  mon < 0 start 
               select output(1)
               printstring("trf:")
               write(m2900_stream, 1);  write(m2900_sub ident, 1)
               write(swab(m2900_p2a), 1); write(swab(m2900_p2b), 1)
               write(d_o lim, 4);  write(d_p lim, 1)
               newline;  select output(0)
            finish 

            to 2900(type, m2900)
         finish 
      end 


      routine  retrieve(record  (con desf) name  d)
         record  (tcpf) name  tcp

         return  if  d_state = not allocated
         if  d_cnsl # 255 start ;   ! cnsl = 255 - disconnected
            tcp == tcpa(d_tcp)
            con index(d_cnsl+tcp_con state ind) = 0
         finish 

        free transient

        d_state = not allocated
#if b
        to bk(d_stream, logged off);    !tell booking server task
#fi
        users = users-1 unless  users<0

         push(free des, d)
      end 


      !! r o u t i n e   lose consoles(all or a specific one)

      routine  lose consoles(integer  x)
         !! throw away connected consoles
         integer  index, i, t, q

         index = tcp_con state ind
         if  x < 0 then  t = max tts-1 and  x = 0 c 
           else  t = x
         cycle  i = x, 1, t
            q = con index(i+index)
            d == con desa(q)
            con index(i+index) = 0

            unless  q = 0 start 
               if  d_state=not allocated start 
                  printstring("itps:attempt to discard free console")
                  write(tcp_tcpn,3); write(q,3)
                  newline
                  continue 
               finish 
               d_cnsl = 255;             ! no messages to the tcp now
               free transient
               unless  d_state >= logging off start 
                  if  d_state = input enabled start 
                     !! log off 2900
                     !! nb: **** the case of "logged on" is not catered for
                     get buffer(send the chop)
                  else 
                     unless  d_state >= logging on then  c 
                       retrieve(d);           ! may re-claim immediately
                  finish 
               finishelseif  d_state=logging off then  retrieve(d)
            finish 
            set prio(1) if  i&15 = 15;      ! don't do too many at once
         repeat 
      end 


      routine  read from am1
         !! itp server has control of the link
         record  (mef) name  mes
         record  (itpf) name  it
         integer  n, flag, sym, lim, type, t, stat, len

         mes == d_hold

         if  mes == null or  d_state = not allocated start 
            printstring("itp:sequence?
")
            p_c1 = 0!128;  to 2900(return control, null)
            return 
         finish 

         d_hold == null

         if  mes_type=0 then  len=bigblockmax-2 else  c 
            len = small block max-2
         it == mes_lev3_itp
         n = it_a(0)
         flag = 0

         if  d_ostate&out p # 0 start 
            lim = d_o lim;  type = out p
         else 
            lim = d_p lim;  type = pmt p
            d_o posx = d_o pos if  n = 1
            !! hold beginning of prompt (temporarily) in oposx
            !!       in case it spans the end of buffer
         finish 

         cycle 
            cycle 
               stat = l_rxs
               exit  if  stat&(ready!xopl) # 0
            repeat 

            if  stat&xopl # 0 start ;       ! xop gone down
               t = 64;  -> skip;        ! send unsuccessfull
            finish 

            sym = l_rxd;                ! read the char
            if  l_rxs&acfy # 0 start ;  ! failed to read
               sym = l_rxd;             ! read it again
               if  l_rxs&acfy # 0 start ; ! hard failure - parity
                  t = 3;  -> skip
               finish 
            finish 

            if  stat&comm bit # 0 start 
               t = 2!128

skip:
               p_c1 = t;                ! long block+accept last
               to 2900(return control, null)
               d_hold == mes;  it_a(0) = n
               return 
            finish 

             if  sym = nl and  d_mode = 0 start 
                 it_a(n) = 13; n = n+1;   ! plant cr
             finish 

            if  d_o pos = d_out lim then  d_opos = -1
            d_o pos = d_o pos+1
            it_a(n) = sym

            if  d_o pos = d_o trig start ;     ! send trigger message
               get buffer(send trig reply)
            finish 

            if  d_o pos = lim start 
               it_hdb2 = terminated
               d_ostate = d_ostate&(¬out p)

reply:
               p_c1 = 0!128;             ! eam1 to reject last char
               if  type = pmt p start 

                  !!      this is actually a prompt  - not output

                  it_hdb2 = d_pmt n;       ! at time of request
                  d_o pos = d_o posx;   ! see comment above at type = pmt p
                  d_ostate = enabld
               else 
                  d_out go = d_out go-1 unless  lose op # 0 or  d_mode = 3
               finish 

               to 2900(return control, null)
               it_cnsl = d_cnsl;  it_hdb1 = text
               if  d_mode = 2 start ;  ! binary
                  it_hdb2 = it_hdb2!bin b
               else 
                  if  d_mode = 3 start ;     ! set mode
                     it_hdb1 = control; it_hdb2 = set mode
                  finish 
               finish 
               it_a(0) = n;             ! itp length
               mes_len = n+header len+1+3;     ! nsi+cnsl+itp+no of chars

               if  d_cnsl = 255 start ;      ! gone away
                  free buffer(mes)
               else 
                   if  type # out p or  lose op = 0 then  c 
                    to gate(put output, mes, 0) else  c 
                    free buffer(mes)
               finish 

               if  (d_ostate > enabld and  d_out go > 0 ) or  c 
                 d_ostate = pmt p!enabld then  get o block
               return 
            finish 

            if  n >= len start 
                !! leave room for a cr/lf sequence
               it_hdb2 = 0
               -> reply
            finish 
            n = n+1


            l_rxs = l_rxs!accept char;    ! accept the last char

         repeat 
      end 

      routine  write to am1

         record  (mef) name  mes
         record  (itpf) name  it
         integer  n, max, char, stat, gah
         constinteger  cr = 13

         mes == d_in mes
         if  d_state # input enabled or  mes == null start 
            p_c1 = 0;              ! terminate
            ->am1 rep;              ! reply to am1 hanmdler
         finish 

         it == mes_lev3_itp
         n = mes_lev3_reserved(1)+1;   ! pos in buffer, when buffer split
         max = it_a(0)
         if  mon < 0 start 
            select output(1); printstring("inp:")
            printstring(string(addr(it_a(0)))); select output(0)
         finish 


         cycle 
            cycle 
               stat = l_rxs
               if  stat&xopl # 0 then  p_c1 = 64 and  ->am1 rep

               if  stat&ready # 0 start 

                 !! l i m i t sent
                 p_c1 = 2;              ! long block
                 mes_lev3_reserved(1) = n-1
am1 rep:         to 2900(return control, null)
                 return 
               finish 

               if  l_txs&ready # 0 then  exit 
            repeat 

            if  n > max start 
               p_c1 = 4;                 ! condition y
               to 2900(return control, null)
                 gah = mes_lev3_reserved(0)
               free buffer(d_in mes);  d_in mes == null
               d_seq bits = d_seq bits+seq inc
#if k
               get buffer(send go ahead)
#else
               if  gah > 3 start 
                  printstring("itps: gah ="); write(gah, 1)
                              !! nasty !
                  newline
                  gah = 2
               finish 
               get buffer(send go ahead) and  gah = gah-1 c 
                 while  gah >= 0
#fi
               return 
            finish 

            cycle 
               char = it_a(n)
               n = n+1
               exit  if  char # cr or  it_hdb2&bin b # 0
            repeat 

            l_txd = char
            if  d_i pos = d_in lim then  d_i pos = -1
            d_i pos = d_i pos+1
         repeat 
      end 



      routine  kick 2900 message(record  (maf) name  log)

         !! this routine sends 'log' to the 2900 by inserting
         !! it in the input q for stream 4, and kicking it if
         !! necessary

         d == d2
         if  (d_hold == null and  d_inp q_e == null) or  d_incnt>5 then  c 
           get buffer(kick message stream)
         push(d_inp q, log)
         d_in cnt = d_in cnt+1
      end 

      routine  tidy message streams
         d2_o state = idle; d3_o state = idle
         while  not  d2_inp q_e == null cycle 
            free buffer(pop(d2_inp q))
         repeat 
      end 



      !!         r e a d   m e s s a g e   f r o m   a m 1


      routine  read message from am1


         record  (maf) name  m
         integer  n, sym, t, stat, lreply, stream, index
         record  (mef) name  mes
         integer  type
         record  (itpf) name  itp

         switch  hlm(1:2)

         ! d3 is allways used
         m == d3_hold; d3_hold == null
         if  m == null or  d3_opos = d3_o lim start 
              printstring("itp: seq2!
")
              t = 0!128; -> reply
         finish 

         !!  (cater for partial block rec'd)
         n = d3_o posx
         if  n = 0 then  d3_in cnt = 0

         cycle 
            cycle 
               stat = l_rxs
               exit  if  stat&(ready!xopl) # 0
            repeat 

            if  stat&xopl # 0 start ;       ! xop gone down
               t = 64;                ! send unsuccessfull
               printstring("itps: xop d
")
               -> skip
            finish 

            sym = l_rxd;                ! read the char
            if  l_rxs&acfy # 0 start ;  ! failed to read
               sym = l_rxd;             ! read it again
               if  l_rxs&acfy # 0 start ; ! hard failure - parity
                  t = 3
                  printstring("itps: parity
")
                  -> skip
               finish 
            finish 

            if  stat&comm bit # 0 start 
              t = 2!128
skip:
               d3_o posx = n; d3_hold == m
reply:
               p_c1 = t;                ! long block+accept last
               to 2900(return control, null)
               return 
            finish 

            if  d3_o pos = d3_out lim then  d3_o pos = -1
            if  d3_o pos = d3_o lim then  -> badm

            d3_o pos = d3_o pos+1

            if  mon < 0 start 
            select output(1)
               printsymbol('i'); write(n, 2); write(sym, 2); space
               printsymbol(sym) if  sym > 32; newline
            select output(0)
            finish 

            m_a(n) = sym;   n = n+1

            if  n = 1 start ;           ! Got the total length
               d3_in cnt = m_a(0);         ! max = 256
               unless  5 <  d3_in cnt <= 64-18 start 
                                        ! nb: SMALL buffer is used
badm:             printstring("***itps: message fails -")
                  write(d3_in cnt, 1); write(d3_o pos, 1); write(d3_out lim, 1)
                  write(d3_o lim, 1); write(type, 1); write(n, 1)
                  printstring(" itp messages lost
")
                  if  n > 0 start 
                     cycle  sym = 0, 1, n
                         write(m_a(sym), 2); newline if  sym&15=15
                     repeat 
                     newline
                  finish 
                  d3_o pos = d3_o lim
                  freebuffer(m)
                  -> reply
               finish 

            else 
               if  n = d3_in cnt then  -> exit3; ! Got the whole message
            finish 

            l_rxs = l_rxs!accept char;    ! accept the last char

         repeat 

exit3:
         d3_o posx = 0;                 ! full message taken
         t = 0!128;                     ! normal+accept last

         if  d3_o pos # d3_o lim start ;     ! Another message waiting
            d == d3
            get buffer(get op block)
         finish 

         type = m_a(1);                 ! max = 256

! ?         x = (8+m_a(4))&x'fffe'
            stream = m_a(2)<<8!m_a(3)
         m_m len = n
         unless  1 <= type <= 2 then  ->badm
         index=(stream-fixed)>>1
         unless  0<=index<=con lim then  ->badm
         d==con desa(index)
         -> hlm(type)

hlm(1):                                 ! Logon Reply

                  lreply = m_a(5)
                  str = string(addr(m_a(6)));   ! copy text out of way
                  mes == m;            ! make it a network buffer
                  mes_len = length(str)+4 +header len
                  mes_lev3_itp_cnsl = d_cnsl
                  mes_lev3_itp_hdb1 = 0
                  mes_lev3_itp_hdb2 = 2
                  mes_lev3_itp_s = str;    ! copy text back in
                  tcp == tcpa(d_tcp)
                  to gate(put output, mes, 0)
               d_out go = d_out go-1
               if  l reply = 0 start 
                  d_state = logged on
               else 
                  d_state = logging off
                  retrieve(d) if  d_cnsl = 255
               finish 
         -> reply

hlm(2):                                 ! setmode out, string at m_a(5)
         if  d_cnsl = 255 start 
            free buffer(m); -> reply
         finish 

         str = string(addr(m_a(5)));      ! copy setmode out of the way
#if k
!see if rawmode mask being used
        if  charno(str,1)=22 start 
           if  charno(str,3) & 64 # 0 then  d_gahs wanted=20 else  d_gahs wanted=3
            if  d_gahs wanted>d_gahs sent then  get buffer(send go ahead)
        finish 
#fi
         mes == m;                      ! change the buffer to an itp one
         itp == mes_lev3_itp
         itp_cnsl = d_cnsl
         itp_hdb1 = control; itp_hdb2 = set mode
         itp_s = str;                     ! put the setmode back in
         mes_len = length(str)+4+header len;       ! hdr+string+string length
         tcp == tcpa(d_tcp);            ! map to tcp description
         to gate(put output, mes, 0);   ! send the buffer
         ->reply;                       ! give control back to am1h

     end 



      !!     w r i t e   m e s s a g e   t o    a m 1

      routine  write message to am1

         record  (maf) name  m
         integer  n, max, am1 reply, stat

         ! allways use d2
         am1 reply = 4;          ! "condition y"

         cycle 

         m == d2_hold
         if  m == null then  m == pop(d2_inp q) and  d2_in cnt = d2_in cnt-1

         if  m == null then  exit 
                          !! terminate with "normal" (shouldnt happen)

         n = d2_o posx;       ! start of block - d2_o posx = 0

         cycle 
            cycle 
               stat = l_rxs

               if  stat&xopl#0 start 
                  d2_hold == m;           ! retain buffer for retry
                  am1 reply = 64;  d2_hold f = n; ->am1 rep
               finish 

               if  stat&ready # 0 start 
                  !! l i m i t sent
                  am1 reply = 2;              ! long block
                  d2_o posx = n;  d2_o pos = max
                  d2_hold == m;         ! retain for later
                 -> am1 rep
               finish 

               if  l_txs&ready # 0 then  exit 
            repeat 


            if  n > m_a(0) start 
               free buffer(m)
               d2_hold == null;  d2_o posx = 0; d2_hold f = 0

               if  d2_inp q_e == null then  ->am1 rep
              exit 
            finish 

            if  mon < 0 start 
            select output(1)
               printsymbol('o'); write(n, 2); write(m_a(n), 2); space
               printsymbol(m_a(n)) if  m_a(n) > 32; newline
               select output(0)
            finish 

            l_txd = m_a(n); n=n+1
         repeat 
         repeat 

am1 rep:
         p_c1 = am1 reply
         to 2900(return control, null)
      end 


#if x
       routine  mon mes(record  (mef) name  mes)
          integer  i, j, k, n
          record  (itpf) name  itp

          k = mes_len;  itp == mes_lev3_itp
          write(k, 1); space; space
          j = 0
          cycle  i = 0, 1, k-1
             if  mon > 0 and  i > 3 start ;        ! 'p' and not header
                n = itp_aa(i)
                printsymbol(n) unless  n = 0 or  n = 4
             else 
                write(itp_aa(i), 1)
                j = j+1;  if  j = 25 then  j = 0 and  newline
             finish 
          repeat 
          newline;  select output(0)
       end 
    
    
       routine  mon p(record  (pe)name  p)
          integer  i
          printstring(" fn ="); write(p_fn, 1)
          printstring(" gate port"); write(p_gate port, 1)
          printstring(" task port"); write(p_task port, 1)
          printstring(" a2"); write(p_a2, 1)
          if  not  p_mes == null start 
             newline; spaces(5)
             write(p_mes_len, 3)
             cycle  i = 1, 1, 25
                write(p_mes_params(i), 2)
             repeat 
          finish 
          newline
       end 
    
    
#fi
endofprogram