!********************************
!*  emas-2900  fep ts29 server  *
!*   file:  fepi_ts29s          *
!********************************
!! stack size = 500
!*
!
! prep options
!
!    k - kent
!    i - new imp compiler
!    s - use streams 8 & 9 as control streams to coexist with itp hdlr
!    b - use kent booking server
!
#if  i
control  x'4001'
include  "b_deimosspecs"
#else
control  1
include  "deimosperm"
#fi
!
begin 
!
conststring  (13) vsn= "ts29...1a "
#datestring
recordformat  am1f(integer  rxs, rxd, txs, txd)
!
ownrecord  (am1f) name  l == 1; ! addr passed by eam1
!
#if  k
constinteger  small block len = 128
#else
constinteger  small block len= 64
#fi
constinteger  small block max= small block len - 11
!
constinteger  big block max= 127; ! < 256 !
!
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(byte  dstart, bytearray  reserved(0:6), (bytearray  data(0:240) orbyte  type))
!
recordformat  mef(record  (mef) name  link, byteinteger  len, type, (record  (itpf) itp orbytearray  params(0:231)))
!
recordformat  m2900f(record  (mef) name  l, byteinteger  len, type, integer  stream, sub ident, p2a, p2b, p3a, p3b, p4a, p4b,
    p5a, p5b, p6a, p6b)
!
recordformat  m2900bf(record  (mef) name  l, byteinteger  len, type, integer  stream, sub ident, byteintegerarray  b(0:19))
!
recordformat  m2900if(record  (mef) name  l, byteinteger  len, type, integer  stream, sub ident, p2a, p2b, string  (15) int)
!
recordformat  m2900cf(record  (mef) name  l, byteinteger  len, type, integer  stream, sub ident, integerarray  pa(0:9))
!
!
recordformat  maf(record  (mef) name  l, byteinteger  mlen, mtype, byteintegerarray  a(0:240))
!
!
recordformat  pe(byteinteger  ser, reply, (integer  a, b, (integer  c orbyte  c1, c2) or  c 
   byte  fn, a2, (record  (mef) name  mes, byte  ts port, task port orstring  (3) facility)))
!
!
recordformat  qf(record  (mef) name  e)
!
!********************************************************
!*  formats of tables, ie stream descriptors, tcps etc  *
!********************************************************
      recordformat  con desf(record  (mef) name  out buf,   { buffer for output}
         record  (mef) name  in buf,                      { buffer for input}
         integer  dindex,                               { internal console number - index to con desa array}
                  o lim, o pos, otrig, oposx,            {posn within buffer}
                  i lim, i pos, prompt ipos, p lim,
                  out lim, in lim,                       {cyclic buffer limits}
                  out go,                                {may be negative}
         byte     pushed,                                {last data input}
                  datatype,                              {0  -> normal}
                  int char,
                  cons no,                               {as on TCP}
                  bits,                                  {status bits-see below}
                  nstate,                                {state of network connection}
                  mode,                                  {0=>iso,2=>bin}
                  port,                                  {ts port number}
         (string  (7) tcp name, user or 
          record  (qf) inp q,                            {data for control strm}
          integer  in cnt, in buf pos, out mess len))
!
!
include  "tsbsp_tscodes"
!
!**************************************************************
!*         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  k
constinteger  ts ser = 16
#else
constinteger  ts ser= 24
#fi
#if b
      constinteger  host bk ser = 25
#fi
constinteger  buffer manager= 17
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'
!***********************************************************
!*               2900  states                               *
!***********************************************************
owninteger  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  fixed= 10; ! 1st available stream
!**************************************************************
!*            network states                                  *
!**************************************************************
constinteger  closed= 0
constinteger  sent name= 1
constinteger  sent pass= 2
constinteger  connected= 3
constinteger  resetting= 4
constinteger  closing= 5
constinteger  sent disc = 6
!
!
! status bits stored in 'bits'
constinteger  allocated= 1
constinteger  is connected= 2
constinteger  os connected= 4
constinteger  is enabled= 8
constinteger  os enabled= 16
constinteger  output pending= 32
constinteger  prompt pending= 64
!
!******************************************
!*  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  send disconnect= 6
!
constinteger  send emas down= 7
#if b
constinteger  send busy = 8
constinteger  send pad params= 9
#else
constinteger  send pad params= 8
#fi
!
constinteger  last itp reason= send pad params
!
constinteger  init facility = 19
!
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 int = 26
constinteger  get big op block= 27
constinteger  kick message stream= 28
!**************************************************************
string (8)fnspec  itos(integer  i)
routinespec  puthex(integer  d)
routinespec  dump(record  (con desf) name  d)
routinespec  from clock
routinespec  crunch
routinespec  to ts(integer  fn, record  (mef) name  mes, integer  flag)
routinespec  to 2900(integer  fn, record  (m2900f) name  m2900)
routinespec  get buffer(integer  reason)
routinespec  free buffer(record  (mef) name  mes)
string  (127) fnspec  unpack(record  (mef) name  mes, integer  no)
routinespec  pack(record  (mef) name  mes, string  (*) name  s)
routinespec  get o block
record  (condesf) mapspec  new slot
routinespec  from ts
routinespec  ucase(string  (*) name  s)
routinespec  set address(string  (*) name  a)
string  (*) mapspec  cleanup(record  (mef) name  mes, integer  max)
routinespec  append(record  (maf) name  m, string  (*) name  s)
routinespec  setup logon request(record  (maf) name  logr, string  (*) name  pass)
routinespec  handle control data(record  (mef) name  mes)
routinespec  qdatain(record  (mef) name  mes)
routinespec  free transient
routinespec  from 2900
routinespec  fill(record  (mef) name  mes, integer  no)
routinespec  from buffer manager(record  (pe) name  p)
routinespec  close connection
routinespec  retrieve(record  (con desf) name  d)
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  translate(record  (maf) name  m, integer  strt)
routinespec  write message to am1
routinespec  mon mes(record  (mef) name  mes)
routinespec  mon p(record  (pe) name  p)
#if b
routinespec  from bk
#fi
!******************************************************
ownrecord  (pe) p
ownrecord  (con desf) name  d
ownrecord  (con desf) name  control d
ownrecord  (qf) name  buffer pool
owninteger  no of buff= 0
!
constinteger  max cons= 2
!
constinteger  initial out go = 1
ownrecord  (con desf) array  con desa(-1:max cons)
!
!-1 is used for the control streams 2 and 3 (or 8 and 9) for the rest
!con desa(i) corresponds to streams fixed+i<<1 and fixed+i<<1+1
!
owninteger  slot scan = 0;       !used in allocation of console slots
owninteger  tp;                  !used in translation of setmodes
ownbytearrayname  t;             !ditto
!
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= 1
!
integer  i, n
!
ownstring  (1) snull= ""
!
!**********************************************
!*      initialisation                        *
!**********************************************
!
change out zero = t3 ser
!
control d == con desa(-1)
!
printstring(vsn)
#if k
printstring("Kent ")
#fi
#if b
printstring("(bk) ")
#fi
#if  i
printstring("new ")
#fi
printstring(datestring); newline
!
#if  i
map hwr(3);                       ! map am1 to seg 3
#else
map hwr(0); ! map am1 to segment 0
#fi
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)_dindex = i for  i = -1, 1, max cons
!
#if  ~s
p_c = 2; ! param for 'here i am'
#else
p_c = 8
#fi
to 2900(here i am, null)
#if  ~s
p_c = 3; ! and claim stream 3
#else
p_c = 9
#fi
to 2900(here i am, null)
!
control d_bits = allocated
d == control d;    !must have d set for get buffer call
get buffer(init facility);!enable "ts29"
!**********************************************
!*           main loop                        *
!**********************************************
alarm(100)
cycle 
   p_ser = 0; poff(p)
!
!
   if  p_reply=0 start 
      from clock
   finishelseif  p_reply=link handler start 
      from 2900
   finishelseif  p_reply=ts ser start 
      from ts
#if b
   finishelseif  p_reply = host bk ser start 
             from bk
#fi
   finishelseif  p_reply=buffer manager then  from buffer manager(p)
repeat 
!
!*************************************************
!*           routines to do the work             *
!*************************************************
!
string (8)fn  itos(integer  i)
!-----------------------------
bytearray  c(0:7)
string (8)s
integer  k, sign
   k=0
   sign = 1
   if  i<0 start 
      i = -i;   sign = -1
   finish 
   cycle 
      c(k)=i-i//10*10+'0';  i=i//10
      k=k+1
   repeatuntil  i=0
   if  sign < 0 then  c(k) = '-' and  k = k+1
   length(s)=k
   for  i=1,1,k cycle 
      charno(s,i)=c(k-i)
   repeat 
   result =s
 end 
!
routine  puthex(integer  d)
!-------------------------------------------------
!
integer  i; 
byteinteger  s; 
!
printsymbol(' '); 
cycle  i = 12,-4,0; 
   s = (d>>i)&x'f'; 
   if  s>9 then  s = s-'0'+'a'-10; 
   printsymbol(s+'0'); 
repeat ; 
end ; 
!
routine  dump(record  (con desf) name  d)
!----------------------------------------
integer  i, n, add
conststring  (5) array  bitstr(0:6) = "allc ","isc ","osc ","ise ", "ose ",
   "outp ","prp "
conststring  (5) array  nstatestr(0:6)="clsd ","name ","pass ","conn ",
   "rst ","disc ","sntd "
!
   if  d_bits&allocated = 0 then  return 
   write(d_dindex,3)
   printsymbol(':')
   printstring(nstatestr(d_nstate))
   for  i=0, 1, 6 cycle 
      if  (d_bits >> i) & 1 #0 then  printstring(bitstr(i))
   repeat 
!
   if  d_dindex>=0 start 
      printstring(d_tcpname); space; printstring(d_user)
   finish 
   newline
   n = 0
   add = addr(d)
   for  i=1, 1, 26 cycle 
      if  n>=16 then  newline and  n=0
      put hex(integer(add))
      n=n+1
      add = add+2
   repeat 
   newlines(2)
end 
!
routine  from clock
!-------------------------------
integer  n, i
   if  host state = down start ;   !see if any consoles to throw off
      n = 0
      for  i=0, 1, max cons cycle 
         if  n > 3 then  exit ;    !never discard more than 3 per clock tick
         d == con desa(i)
         if  d_bits & allocated # 0 start 
            if  d_nstate = closed start 
               retrieve(d)
           finishelse  if  d_nstate # closing and  d_nstate # sent disc start 
               free transient
               get buffer(send emas down)
               get buffer(send disconnect)
               n = n+1
            finish 
         finish 
      repeat 
   finish 
!
   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
         select output(1)
         write(no of buff, 4); write(users,1);  newline
         for  i = -1, 1, max cons cycle 
            dump(con desa(i))
         repeat 
         close output
         printstring("Done"); newline
      finish 
      if  int='C' start 
         select output(1)
         close output
         printstring("Done
")
      finish 
!
      int = 0
   finish 
   alarm(100)
end 
!
routine  crunch
!-------------------------------
!--------------
   printstring("ts29: Bad buffer ***** dump fep ********
")
   *=k'104001'; ! emt wait
end 
!
routine  to ts(integer  fn, record  (mef) name  mes, integer  flag)
!-------------------------------
!
   unless  mes==null start 
      if  (addr(mes)&k'160000'#k'100000' and  addr(mes)&k'160000'#k'120000') orc 
         addr(mes)&k'77'#0 then  crunch
   
   finish 
!
   if  fn=put output start 
      if  mon<0 start 
         select output(1)
         printstring("To Tcp "); mon mes(mes)
      finish 
   finish 
!
   p_ser = ts ser; p_reply = own id
   p_fn = fn; p_ts port = d_port; p_mes == mes
   p_a2 = flag
   p_task port = d_dindex
   if  mon#0 start 
      select output(1); spaces(5)
      printstring("ts29: to ts:"); mon p(p)
      select output(0)
   finish 
   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 immediately*
   !*         otherwise hold the activity until it arrives*
   !*******************************************************
!
   if  reason=get big op block then  type = 0 else  type = 1
   p_c2 = reason
   p_a2 = d_dindex
   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 = no of buff-1; from buffer manager(p)
   finish 
end 
!
routine  free buffer(record  (mef) name  mes)
!-------------------------------
   record  (pe) p
!
   if  (addr(mes)&k'160000'#k'100000' and  addr(mes)&k'160000'#k'120000') orc 
       addr(mes)&k'77'#0 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 
!
!
string  (127) fn  unpack(record  (mef) name  mes, integer  no)
!--------------------------------------------------------------
   integer  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)))
   finishelseresult  = ""
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 
!
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  get buffer(get big op block) c 
      else  get buffer(get op block)
end 
!
record  (condesf) map  new slot
!------------------------------
integer  i
   i = slot scan
   cycle 
      slot scan = slot scan+1
      if  slot scan>max cons then  slot scan = 0
      d == con desa(slot scan)
      if  d_bits=0 then  d_bits=allocated and  result  == d
   repeatuntil  slot scan=i
   result ==null
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
   d==con desa(index)
   index=index*2 + fixed
   if  p_fn=logon reply start 
      if  p_a2=bkaccept start ;   !send logon request to the host
         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_nstate=closed start 
            retrieve(d)
         else 
            d_bits = d_bits&(¬(isconnected!osconnected));  !they never were really connected
            get buffer(send busy)
            get buffer(send disconnect)
         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;     !user name in m_a(4) onwards, as string
      kick 2900 message(m);       !NB corrupts d
   finish 
!
end 
#fi
!
routine  from ts
!-------------------------------
   record  (mef) name  mes
   integer  fn, cno, int char
   ownstring  (4) mes emas="emas"
   ownstring  (21) mes no free console slots="No free console slots"
   ownstring  (5) quality="W=1/1"
   switch  fns(connect:reset)
   string  (63) called
!
   fn = p_fn
   mes == p_mes
   if  mon<0 start 
      selectoutput(1)
      printstring("From ts:")
      monp(p)
      if  fn=connect or  fn=input here then  mon mes(mes)
      selectoutput(0)
   finish 
!
   unless  connect<=fn<=reset start 
      printstring("ts29:Illegal fn from tsbsp"); write(fn,1); newline
      unless  mes==null then  free buffer(mes)
      return 
   finish 
!
   if  fn#connect start ; !verify state and port numbers
      cno = p_task port
      if  0<=cno<=maxcons start 
         d == con desa(cno)
         if  d_nstate=closed or  d_bits&allocated=0 start 
            printstring("ts29:illegal message from tsbsp")
err:
            write(fn, 1); write(p_ts port, 1); write(d_bits, 1); newline
            unless  mes==null then  free buffer(mes)
            return 
         finish 
      else 
         printstring("ts29:illegal console number from tsbsp");
         write(cno, 1); ->err
      finish 
   finish 
!
   ->fns(fn)
!
fns(connect):
   called = unpack(mes, 2)
   d == new slot
   if  d==null start ; !no free slots
      mes_len = 0
      pack(mes, mes emas)
      pack(mes, mes no free console slots)
      d == control d; !use this one as it hasn't a network connection
      d_port = p_ts port
      to ts(disconnect, mes, ts err busy)
      return 
   finish 
   cno = d_dindex
   users = users + 1
   d_port = p_ts port
   set address(called);     !decypher tcp name and console number from address
                            !and store into d
   mes_len = 0
   pack(mes, mes emas)
   pack(mes, quality); !quality = W=1/1 only 1 input buffer
   pack(mes, snull)
   to ts(accept call, mes, 0)
   d_pushed = 1; !initialise state of data stream in
   d_out go = initial out go
   if  host state=down start 
      d_nstate = connected
      get buffer(send emas down)
      get buffer(send disconnect)
      return 
   finish 
   get buffer(send pad params)
   get buffer(send name prompt); !normal logon attempt - send User: prompt
   d_nstate = sent name
   return 
!
fns(input here):
   to ts(enable input, null, 1)
   unless  d_nstate<=connected then  free buffer(mes) andreturn 
!
   if  d_pushed#0 start ; !last data was pushed
      if  mes_len=0 start 
         printstring("ts29:no data type byte from ")
         printstring(d_tcpname); write(d_consno,1);  newline
      else 
         d_data type = mes_itp_type
      finish 
      mes_itp_dstart = 1
   else 
      mes_itp_dstart = 0
   finish 
   d_pushed=p_a2
   if  d_datatype=0 then  qdatain(mes) else  handle control data(mes)
   return 
!
fns(disconnect):
   unless  mes==null then  free buffer(mes)
   if  d_nstate=closing start ; !stream now closed
      if  d_bits&(isconnected+osconnected)=0 then  retrieve(d)
   else 
      to ts(disconnect, null, 1)
      if  d_bits&(isconnected+osconnected)=0 start 
         retrieve(d)
      else 
         d_intchar = 'Y'
         get buffer(send int)
      finish 
   finish 
      d_nstate = closed
   return 
!
fns(enable output):
   if  d_nstate>connected thenreturn 
   if  d_out go<=0 start ; !output blocked
      d_out go = d_out go+p_a2
      if  d_out go>0 and  d_bits&(prompt pending!output pending)#0 then  get o block
      return 
   finish 
   d_out go = d_out go+p_a2
   return 
!
fns(expedited data):
   if  mes==null start 
      int char = p_a2
   else 
      int char = mes_params(0)
      free buffer(mes)
   finish 
   if  int char=0 then  intchar = 'A'
   if  d_nstate>connected or  d_bits&isconnected=0 thenreturn 
   d_int char = int char
   get buffer(send int)
   return 
!
fns(reset):
   unless  mes==null then  free buffer(mes)
   if  d_nstate=closing thenreturn 
   if  d_nstate=sent disc start 
      to ts(disconnect, null, tserr reset)
      free transient
      d_nstate = closing
      return 
   finish 
   if  d_nstate=resetting start 
      d_nstate = connected
   else 
      to ts(reset, null, 1)
      d_intchar = 'C';      !send int C to emas if nstate ok
      if  d_nstate=connected then  get buffer(send int)
   finish 
   d_pushed = 1
   if  d_out go<=0 start ; ! output was blocked
      if  d_bits&osenabled#0 then  get o block
   finish 
   d_out go = initial out go
   return 
!
end 
!
!
routine  ucase(string  (*) name  s)
!-------------------------------
   integer  i
   for  i = 1, 1, length(s) cycle 
      if  'a'<=charno(s, i)<='z' then  charno(s, i) = charno(s, i)-'a'+'A'
   repeat 
end 
!
routine  set address(string  (*) name  a)
!-------------------------------
!takes a ts address probably of the form tcpxyz/ts29/n where
!n is the console number in hex (single digit) This form is checked
!and if ok the tcpname and console number are stored in the console
!descriptor
   string  (63) addr, add2
   integer  c
   ucase(a)
   if  a->addr.("/TS29/").add2 start 
      if  length(add2)=1 start ; !console number in hex
         c = charno(add2, 1)
         if  '0'<=c<='9' then  c = c-'0' elseif  'A'<=c<='F' then  c = c-'A'+10 else  c = 0
      else 
         c=0
      finish 
!remove initial address fields separated by /
      while  addr->add2.("/").addr cycle ; repeat 
      if  length(addr)>7 then  length(addr) = 7
      d_tcp name = addr
      d_consno = c
   else 
      d_tcp name = "ANON"
      d_consno = d_dindex; !keeps them unique
   finish 
end 
!
string  (*) map  cleanup(record  (mef) name  mes, integer  max)
!-------------------------------------------------
!mes is a data buffer from the network. trailing cr lf are removed
!and a string pointer is returned (corrupting the byte before the data
!as the length)
   integer  ds, l
   ds = mes_itp_dstart
   l = mes_len-ds
   if  l>0 and  mes_itp_data(ds+l-1)=13 then  l = l-1; !remove trailing cr
   if  l>max then  l=max
   mes_itp_data(ds-1) = l; !make it look like a string
   result  == string(addr(mes_itp_data(ds-1)))
end 
!
routine  append(record  (maf) name  m, string  (*) name  s)
!-------------------------------
!append s to the buffer m which is destined for the 2900
!
   integer  x
   x = m_mlen
   string(addr(m_a(x))) = s
   m_mlen = x+length(s)+1
end 
!
routine  setup logon request(record  (maf) name  logr, string  (*) name  pass)
!--------------------------------------------------------------------------
!
!have got buffer to store logon request info in
!
   logr_a(1) = 1
   logr_a(2) = 0
   logr_a(3) = d_dindex<<1+fixed
   string(addr(logr_a(4))) = d_tcp name."::".itos(d_consno).":"."emas"
                                !blank field above is for the terminal speed
   logr_mlen = logr_a(4)+4+1
   append(logr, d_user)
   append(logr, pass)
   logr_a(0) = logr_mlen-1
end 
!
routine  handle control data(record  (mef) name  mes)
!----------------------------------------------------
   free buffer(mes)
end 
!
routine  qdatain(record  (mef) name  mes)
!-------------------------------
!handle buffer of data
   integer  index, l, i
   string  (8) pass
!
   if  d_nstate=connected start ; !normal input data
      if  mes_itp_dstart>=mes_len start ; !empty buffer
         free buffer(mes)
         return 
      finish 
      if  d_in buf==null start 
         d_inbuf == mes
         if  d_bits&isenabled#0 then  get buffer(low level ip transfer)
      else 
!see if new buffer can be copied into existing one
         l = d_inbuf_len
         if  mes_len+l <240 start 
            for  i = mes_itp_dstart, 1, mes_len-1 cycle 
               d_inbuf_itp_data(l) = mes_itp_data(i)
               l = l+1
            repeat 
            free buffer(mes)
            d_inbuf_len = l
         else 
            printstring("ts29:Extra input!!")
            free buffer(mes)
         finish 
      finish 
      return 
   finish 
   if  d_nstate=sent name start ; !this should be the user name
      d_nstate = sent pass
      get buffer(put echo off)
      get buffer(send pass prompt)
      d_user = cleanup(mes, 7)
      return 
   finish 
   if  d_nstate=sent pass start 
      pass = cleanup(mes, 8);    !copy password out of buffer
      d_nstate = connected
      get buffer(send nl)
      get buffer(put echo on)
      d_bits=d_bits!(isconnected!osconnected)
      setup logon request(mes, pass)
#if b
      p_mes == mes
      to bk(d_dindex, can i logon)
#else
      index = d_dindex<<1+fixed
      p_c = index
      to 2900(here i am, null); !tell am1 handler about new stream
      p_c = index+1
      to 2900(here i am, null);
      kick2900 message(mes); !NB corrupts d
#fi
      return 
   finish 
end 
!
routine  free transient
!-------------------------------
   ifnot  d_in buf==null then  free buffer(d_in buf) and  d_in buf == null
   ifnot  d_out buf==null start 
      free buffer(d_out buf); d_out buf == 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
!
   integer  stream, sub ident, state, trig, mode, i
   integer  type, p2b
   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 
   if  stream>=fixed then  d == con desa((stream-fixed)>>1) else  d == control d
   ->link fns(p_fn)
!
!
link fns(interf addr):
                                      ! interface addr from eam5
#if  i
   l == record(addr(p_mes)&k'17777'!k'060000'); ! put in seg 3
#else
   l == record(addr(p_mes)&k'17777'); ! force to seg 0
#fi
   return 
!
!
link fns(do output):
                                      ! -> 11/34
#if  ~s
   if  stream = 3 then  read message from am1 else  c 
#else
   if  stream = 9 then  read message from am1 else  c 
#fi
   read from am1
!         ->d mon
   return 
!
link fns(do input):
                                      ! -> 2900
#if  ~s
   if  stream = 2 then  write message to am1 else  c 
#else
   if  stream = 8 then  write message to am1 else  c 
#fi
   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, max cons
      d == con desa(i)
      d_bits = d_bits & allocated;     !clear all the other bits
   repeat 
   host state = down
   users = 0
   return 
!
!
link fns(message):
   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<10 start 
         if  state=connecting start 
            control d_bits=control d_bits ! (isconnected+osconnected)
            !! initial logon stream connected
            host state = up
            printstring("logon stream connected
")
            users = 0
         else 
            if  state=enabling start 
               printstring("ts29:logon stream enabled ")
               if  stream&1=0 start 
                  d_bits = d_bits!isenabled
                  printstring("(input)"); newline
               else 
                  d_bits = d_bits!osenabled
                  d_outlim = m2900_p2b
                  printstring("(output)"); newline
               finish 
            finish 
!
            if  state=disconnecting start 
               control d_bits = allocated;   !clear all bits but allocated
               host state = down
               printstring("logon stream disconnected
")
               tidy message streams
            finish 
         finish 
      else 
!
         if  d_bits&allocated=0 start 
            printstring("ts29:attempt to access unallocated stream")
            write(stream, 1); write(d_nstate, 1); write(d_bits, 6); newline
            ->send reply
         finish 
!
         if  state=enabling start ; ! 1st interesting condition
            if  stream&1=0 start ;              !enable input stream
               d_bits=d_bits!isenabled
               if  d_nstate=closed start 
                  type=1;                      !abort the stream
               else 
                  d_in lim=m2900_p2b
                  d_i pos=m2900_p3b
                  unless  d_inbuf==null then  get buffer(low level ip transfer)
               finish 
            else ;                             !enable output stream
               d_bits=d_bits!osenabled
               d_outlim=m2900_p2b
               d_o pos=m2900_p3b
               d_mode = mode>>4; ! 0-iso,x'20'-bin,x'30'-cont
            finish 
         finishelseif  state=disconnecting start 
            if  stream&1=0 start ;             !disconnect input stream
               d_bits=d_bits&(¬(isconnected+isenabled))
            else ;                             !disconnect output
               d_bits=d_bits&(¬(osconnected+osenabled))
            finish 
            if  d_bits&(osconnected+isconnected)=0 start 
               close connection
!
            finish 
!
         finishelseif  state=aborting or  state=suspending start ;   !stop streams
            if  stream&1=0 start 
               d_bits=d_bits&(¬isenabled)
               if  state=aborting start 
                  unless  d_inbuf==null then  free buffer(d_inbuf) andc 
                     d_inbuf==null
                  to ts(reset, null, 0)
                  d_nstate=resetting
                  d_out go = initial out go
               finish 
            else 
               d_bits=d_bits&(¬(osenabled+output pending+prompt pending))
               unless  d_outbuf==null then  free buffer(d_outbuf) andc 
                   d_outbuf==null
            finish 
         finish 
      finish 
      m2900_p2a = 0; m2900_p2b = 0
send reply:
      to 2900(low level control, m2900)
      if  type#0 then  d_int char='Y' and  get buffer(send int); !chop the stream
      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 
!the input is at the trigger position ie no type ahead discernible now
!get the prompt
         d_p lim = m2900_p2b
         d_prompt i pos=d_ipos;         !remember current value so can check
                                        !there's been no type ahead when the
                                        !prompt is read
         if  d_bits&(prompt pending+output pending+osenabled)=osenabled start 
            d_outbuf==m2900;            !save the buffer
            d_bits=d_bits!prompt pending
            get buffer(low level op transfer)
            return 
         finish 
         d_bits=d_bits!prompt pending
      finish 
      free buffer(m2900); ! past that position already
   else 
      !************************
      !*  output  stream      *
      !************************
#if  ~s
      if  stream=3 start 
#else
         if  stream = 9 start 
#fi
!
!
         !! update of pointer on message stream
         p2b = m2900_p2b
         free buffer(m2900)
         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 orc 
                (d_opos<d_olim andnot  d_opos<d_otrig<=d_olim) orc 
                (d_opos>d_olim and  d_olim<=d_otrig<=d_opos)
         finish 
!
         d_bits=d_bits&(¬prompt pending);  !discard prompt
         if  d_bits&output pending=0 and  d_opos#d_olim start 
            d_bits=d_bits!output pending
            if  mon<0 start 
               select output(1)
               printstring("o/p: go, size:")
               write(d_out go, 1); newline
               select output(0)
            finish 
            if  d_out go>0 start ; ! allowed to send
               ifnot  d_out buf==null start 
                  free buffer(m2900)
               else 
                  d_out buf == m2900
               finish 
!
               get buffer(low level op transfer)
               return 
            finish 
         finish 
         free buffer(m2900)
      finish 
   finish 
end 
!
routine  fill(record  (mef) name  mes, integer  no)
!-------------------------------
   integer  i, pt
!
   ownbyteintegerarray  pts(1:last itp reason)
!
!the itp messages have a length followed by the ts29 data type byte followed
!by the data. The pts array is initialised on the first call to index the
!messages.
#if b
   ownbyteintegerarray  itp message(1:106) =
#else
   ownbyteintegerarray  itp message(1:65) =
#fi
   6,0,'U','s','e','r',':',;                  !name prompt
   6,0,'P','a','s','s',':',;                  !password prompt
   4,128,2,2,1,;                              !echo on
   4,128,2,2,0,;                              !echo off
   3,0,13,nl,;                                !newline
   2,128,1,;                                  !disconnect (invitation to clear)
   16,0,13,nl,'*','*','2','9','0','0',' ',
   'D','o','w','n',13,nl,;                    !**2900 Down
#if b
   40,0,13,nl,'*','*','*','S','o','r','r','y',' ',;    !Sorry no free consoles
          't','h','e','r','e',' ','a','r','e',' ','n','o',' ',
          'f','r','e','e',' ','c','o','n','s','o','l','e','s',13,nl,
#fi
   16,128,2,2,1,;                             !pad params- echo on
            3,2,;                             !forward on cr
            7,1,;                             !transmit Interrupt on break
            9,0,;                             !no pad after cr
            10,80,;                           !line fold after 80
            12,0,;                            !flow control off
            13,4;                             !lf inserted after echoed cr
!
   if  pts(1)=0 start ;    ! initialise pts array
      pt=1
      for  i=1,1,last itp reason cycle 
         pts(i)=pt
         pt=pt+itp message(pt)+1
      repeat 
   finish 
   pt = pts(no)
!
   string(addr(mes_itp_reserved(6)))=string(addr(itp message(pt)))
   mes_len=itp message(pt)
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
!
   reason = p_c2; ! get reason for calling
   n = p_a2
   if  n>=254 then  n = n-256
   d == con desa(n); ! get console descriptor
   if  mon<0 start 
      select output(1); printstring("from buff:")
      write(p_ts port, 1); write(n, 1); write(reason, 1)
      write(d_dindex, 1); write(d_nstate, 1)
      newline; select output(0)
   finish 
!
   if  d_bits&allocated=0 then  free buffer(p_mes) and  return 
!
   if  reason=init facility start 
      string(addr(p_mes_params(0)))="TS29"
      to ts(enable facility, p_mes, 1)
      return 
   finish 
!
   if  reason<=last itp reason start 
      if  sent name<=d_nstate<=connected or  d_nstate=resetting start 
         fill(p_mes, reason); ! insert the message
!
         to ts(put output, p_mes, 1);    !always push the data
         d_out go=d_out go-1
         if  reason=send disconnect then  d_nstate=sent disc
      else 
         free buffer(p_mes)
      finish 
!
   else 
!
      if  reason=get op block or  reason=get big op block start 
         if  d_bits&osenabled=0 then  free buffer(p_mes) and  return 
         unless  d_out buf==null then  free buffer(d_out buf)
         d_out buf == p_mes
         get buffer(low level op transfer)
         return 
      finish 
!
      !! message to 2900 reason
      m2900 == p_mes
      m2900_stream = d_dindex<<1+fixed+reason&1
      m2900_sub ident = 10
!
#if  ~s
      if  d_dindex<0 then  m2900_stream = 2+(reason&1)
#else
      if  d_dindex < 0 then  m2900_stream = 8+(reason&1)
#fi
!
      if  reason=low level op transfer start 
         mes == d_out buf
         if  mes==null then  free buffer(p_mes) and  return 
         ! kill op done, so ignore tran request
         mes_itp_dstart=1;              !start of data index
         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 int start 
         mi == m2900; mi_sub ident = 0; type = send data
         mi_p2a = -1; mi_p2b = -1
         length(mi_int) = 1
         charno(mi_int, 1) = d_int char
      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  close connection
!------------------------
switch  st(closed:sent disc)
   free transient
   ->st(d_nstate)
st(closed):
   retrieve(d)
   return 
st(sent pass):
st(sent name):
st(connected):
   get buffer(send disconnect)
   return 
st(resetting):
st(sent disc):
   to ts(disconnect, null, tserr crash)
   d_nstate = closing
   return 
st(closing):
end 
!
routine  retrieve(record  (con desf) name  d)
!-------------------------------
!
   if  d_bits&allocated=0 start 
      printstring("ts29:attempt to free deallocated slot")
      write(d_dindex, 1);  newline
      return 
   finish 
   free transient
!
   d_bits=0
#if b
        to bk(d_dindex, logged off);    !tell booking server task
#fi
   users = users-1
   if  users<0 start 
      printstring("ts29:users count negative")
      newline
      users = 0
   finish 
!
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, prompt, t, stat, len
!
   mes == d_out buf
!
   if  mes==null or  d_bits&osenabled=0 start 
      printstring("ts29:sequence?
")
      p_c1 = 0!128; to 2900(return control, null)
      return 
   finish 
!
   d_out buf == null
!
   if  mes_type=0 then  len = bigblockmax-2 else  len = small block max-2
   it == mes_itp
   n = it_dstart
   flag = 0
!
   if  d_bits&output pending#0 start 
      lim = d_o lim; prompt=0
   else 
      lim = d_p lim; prompt=1
      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
         exitif  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_out  buf == mes; it_dstart = n
         return 
      finish 
!
      if  sym=nl and  d_mode=0 start 
         it_data(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_data(n) = sym
      n = n+1
!
      if  d_o pos=d_o trig start ; ! send trigger message
         get buffer(send trig reply)
      finish 
!
      if  d_o pos=lim start 
         d_bits=d_bits&(¬output pending)
!
reply:
         p_c1 = 0!128; ! eam1 to reject last char
!
         to 2900(return control, null)
         mes_len = n
         it_type = 0;          !not control data
!
         if  d_nstate#connected and  d_nstate#resetting start 
            free buffer(mes)
         else 
            if  prompt#0 start 
   
               !!      this is actually a prompt  - not output
   
               d_o pos = d_o posx; ! see comment above at type = pmt p
               d_bits=d_bits&(¬prompt pending)
               if  d_prompt ipos#d_ipos start ;    !type ahead
                  free buffer(mes);                !discard the prompt
               else 
                  to ts(put output, mes, 1);       !pushed
                  d_out go = d_out go-1
               finish 
            else 
               if  d_mode=3 start ;               !set mode
                  it_type = n-1;                !make it look like string
                  translate(mes, 8);            !m_a(8) == mes_itp_type
                                                !translate accesses the buffer as maf
               finish 
               to ts(put output, mes, 1)
               d_out go = d_out go-1
            finish 
         finish 
!
         if  d_out go>0 and  d_bits&(prompt pending!output pending)#0 thenc 
            get o block
         return 
      finish 
!
      if  n>=len start 
         !! leave room for a cr/lf sequence
         ->reply
      finish 
!
!
      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
   constinteger  cr= 13
!
   mes == d_in buf
   if  d_bits&isenabled=0 or  mes==null start 
      p_c1 = 0; ! terminate
      ->am1 rep; ! reply to am1 hanmdler
   finish 
!
   it == mes_itp
   n = it_dstart
   max = mes_len
   if  mon<0 start 
      select output(1); printstring("inp:")
      it_data(n-1)=max-n
      printstring(string(addr(it_data(n-1)))); newline; 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
            it_dstart = n
am1 rep:
            to 2900(return control, null)
            return 
         finish 
!
         if  l_txs&ready#0 thenexit 
      repeat 
!
      if  n>=max start 
         p_c1 = 4; ! condition y
         to 2900(return control, null)
         free buffer(d_in buf); d_in buf == null
         return 
      finish 
!
!
      char = it_data(n)
      char = nl if  char=cr; ! forwarding on cr, with no lf
      n = n+1
!
      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 == control d
   if  (d_out buf==null and  d_inp q_e==null) or  d_incnt>5 then  get buffer(kick message stream)
   push(d_inp q, log)
   d_in cnt = d_in cnt+1
end 
!
routine  tidy message streams
!-------------------------------
   control d_bits=allocated
   whilenot  control d_inp q_e==null cycle 
      free buffer(pop(control d_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
   record  (mef) name  mes
   integer  type
   record  (itpf) name  itp
   string  (40) str
!
   switch  hlm(1:2)
!
   ! control d is always used
   m == control d_out buf; control d_outbuf == null
   if  m==null or  control d_opos=control d_o lim start 
      printstring("ts29: seq2!
")
      t = 0!128; ->reply
   finish 
!
   !!  (cater for partial block rec'd)
   n = control d_o posx
   if  n=0 then  control d_out mess len = 0
!
   cycle 
      cycle 
         stat = l_rxs
         exitif  stat&(ready!xopl)#0
      repeat 
!
      if  stat&xopl#0 start ; ! xop gone down
         t = 64; ! send unsuccessfull
         printstring("ts29: 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("ts29: parity
")
            ->skip
         finish 
      finish 
!
      if  stat&comm bit#0 start 
         t = 2!128
skip:
         control d_o posx = n; control d_out buf == m
reply:
         p_c1 = t; ! long block+accept last
         to 2900(return control, null)
         return 
      finish 
!
      if  control d_o pos=control d_out lim then  control d_o pos = -1
      if  control d_o pos=control d_o lim then  ->badm
!
      control d_o pos = control d_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
         control d_out mess len = m_a(0); ! max = 256
         unless  5<control d_out mess len<=small block len-18 start 
            ! nb: SMALL buffer is used
badm:
            printstring("***ts29: message fails -")
            write(control d_out mess len, 1); write(control d_o pos, 1); write(control d_out lim, 1)
            write(control d_o lim, 1); write(type, 1); write(n, 1)
            printstring(" ts29 messages lost
")
            if  n>0 start 
               cycle  sym = 0, 1, n
                  write(m_a(sym), 2); newline if  n&15=15
               repeat 
               newline
            finish 
            control d_o pos = control d_o lim
            ->reply
         finish 
!
      else 
         if  n=control d_out mess len then  ->exit3; ! Got the whole message
      finish 
!
      l_rxs = l_rxs!accept char; ! accept the last char
!
   repeat 
!
exit3:
   control d_o posx = 0; ! full message taken
   t = 0!128; ! normal+accept last
!
   if  control d_o pos#control d_o lim start ; ! Another message waiting
      d == control d
      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
   d == con desa((stream-fixed)>>1)
!
   ->hlm(type)
!
hlm(1):
                                      ! Logon Reply
   lreply = m_a(5)
   if  d_bits&allocated=0 start 
      printstring("ts29:Invalid logon reply")
      dump(d)
      free buffer(m)
      ->reply
   finish 
   if  d_nstate=closed start 
      if  lreply#0 then  retrieve(d);   !logon failed anyway
                                        !successful logon is trapped
                                        !when the streams are enabled
      free buffer(m)
      ->reply
   finish 
!
   str = string(addr(m_a(6))); ! copy text out of way
   mes == m; ! make it a network buffer
   mes_len = length(str)+1
   string(addr(mes_itp_type)) = str
   mes_itp_type = 0
   to ts(put output, mes, 1)
   d_out go = d_out go-1
   if  l reply#0 start 
      d_bits = d_bits&(¬(isconnected!osconnected))
      get buffer(send disconnect); ! immediate request to go
   finish 
   ->reply
!
hlm(2):
                                      ! setmode out, string at m_a(5)
   translate(m, 5);    !convert to ts29 form
   if  connected<=d_nstate<=resetting start 
      to ts(put output, m, 1)
      d_out go = d_out go-1
   else 
      free buffer(m)
   finish 
   ->reply; ! give control back to am1h
!
end 
!*******************************************************************
!   code to translate setmodes (more or less)
!*******************************************************************
!
routine  stuff(integer  code, val)
!---------------------------------
!insert code and val into setmode array
!the array name t and pointer tp are %owns
   t(tp)=code
   t(tp+1)=val
   tp=tp+2
end 
!
routine  settabs(record  (maf) name  m, integer  mp)
!---------------------------------------------------
integer  i, code
   code = 54;    !code for first tab posn
   for  i=1, 1, 7 cycle 
      stuff(code, m_a(mp))
      mp = mp+1
      code = code+1
   repeat 
end 
!
integerfn  superset(integer  base)
!---------------------------------
!base is the address of the first of 16 bytes making up the full rawmode
!bit map. The superset of ts29 data forwarding options is constructed and
!returned as the function value.
!the ts29 options are defined by the arrays below which are slightly
!compressed versions of the bitmaps of the ts29 options
!start and end are the positions of the non-zero bytes of the
!compressed bitmaps, the index corresponds to the ts29 option bit
!the sections of the bitmaps are stored one after the other in mask
!
constbytearray  start(0:7) = 6, 1, 0, 2, 0, 1, 0, 4
constbytearray    end(0:7) = 15,1, 3, 15,0, 1, 3, 15
constbytearray  mask(0:46) = c 
16_ff, 16_03, 16_fe, 16_ff, 16_ff, 16_07, 16_fe, 16_ff, 16_ff, 16_07,;!A-Z a-z 0-9
16_20,                                                               ;! CR
16_e0, 16_00, 16_00, 16_08,                         ;!ESC BEL ENQ ACK
16_04, 16_01, 0(11), 16_80,                         ;!DEL CAN DC2
16_18,                                              ;!ETX EOT
16_1e,                                              ;!HT LF VT FF
16_07, 16_c1, 16_fb, 16_f6,                         ;!other controls
16_ff, 16_ff, 16_00, 16_fc, 16_01, 16_00, 16_00, 16_f8, 16_01, 16_00,
       16_00, 16_78                                 ;!everything else
!
integer  bit, l, i, j, bits
   bit = 1;                  !individual ts29 option - gets shifted left
   bits = 0;                 !actual ts29 option
   l = 0;                    !for indexing into mask array
   for  i = 0, 1, 7 cycle 
      for  j = start(i), 1, end(i) cycle ;          !next ts29 option
         if  byteinteger(base+j) & mask(l) # 0 then  bits = bits ! bit
         l = l+1
      repeat 
      bit = bit<<1
   repeat 
   result  = bits
end 

routine  translate(record  (maf) name  m, integer  strt)
!----------------------------------------
!translate itp setmode starting at m_a(strt) to ts29
record  (mef) name  mes
integer  mp, max, code, word, val
constinteger  copy = 0
constinteger  ignore = 1
constinteger  copyinv = 2
constinteger  tabs = 3
constinteger  grout = 4
constinteger  tty = 5
constinteger  video = 6
constinteger  bulk = 7
constinteger  stop = 8
!
constinteger  maxcode = 23
!
ownbytearray  action(0:maxcode)=stop,copy,copy,copy,ignore,ignore,
   ignore,copy,copy,copyinv,tabs,grout,ignore,tty,copy,video,stop,
   copy,ignore,ignore,stop,copyinv,bulk,video
!
ownbytearray  tcode(0:maxcode)=0,2,51,10,0,0,
   0,16,17,15,22,10,0,0,9,52,0,
   5,0,0,0,53,0,52
!
switch  operation(copy:stop)
byteintegerarray  tt(0:50)
   tp = 1;       !setup %owns to access tt array, tt(0) will hold the length
                 !so that string copy can be used to copy back into m
   t == tt
   mp = strt+1;     !start of data part of itp setmode
   max = m_a(strt)+strt
   while  mp<max cycle 
      code = m_a(mp)
      if  code<=maxcode start 
         val = m_a(mp+1)
         mp = mp+2;          !default increment
         ->operation(action(code))
!
operation(ignore):
         continue 
!
operation(copy):
cpy:
         stuff(tcode(code), val)
         continue 
!
operation(copyinv):
         if  val#0 then  val = 0 else  val = 1
         ->cpy
!
operation(tabs):
         settabs(m, mp-1);    !start of tab vector
         mp = mp+6
         continue 
!
operation(grout):;               !graphical output
         if  val#0 then  val = 0 else  val = 80;    !default line len??
         ->cpy
!
operation(tty):
         stuff(10, 80);       !line len => graph mode off
         stuff(15, 1);        !line editing on
         continue 
!
operation(video):
         if  val#0 then  val = 2
         ->cpy
!
operation(stop):
         exit 
!
operation(bulk):
!
!bulk setmode, mp has been incremented by 2 already so mp-1 addresses
!the first byte of the parameters
!
         word = m_a(mp-1);    !byte 1 bits
         if  word&2=0 then  val = 1 else  val = 0
         stuff(2, val)
!
         if  word&4#0 then  m_a(mp+3)=0;    !line len=0=>graph output
!
         if  word&16=0 then  val = 1 else  val = 0
         stuff(15, val);                    !editing disabled
!
         word = m_a(mp);      !byte 2
!
         val=word&1;          !flow control on/off
         stuff(5, val)
!
         if  word&8=0 then  val = 0 else  val = 2;     !video mode
         stuff(52, val)
!
         if  word&32=0 then  val = 1 else  val = 0;   !hw tabs
         stuff(53, val)
!
!if rawmode bit set then construct superset of bit map with ts29 data
!forwarding char options else just forward on CR
         if  (word&64)#0 then  val=superset(addr(m_a(mp+17))) else  val=2
         stuff(3, val)
!
         stuff(51, m_a(mp+4));    !page len
         stuff(10, m_a(mp+3));    !line len
         stuff(16, m_a(mp+15));   !DEL char
         stuff(17, m_a(mp+16));   !CAN char
         settabs(m, mp+6)
         stuff(9, m_a(mp+1));     !cr pads
         exit 
      finish 
   repeat 
   mes == m;       !convert to ts29 format
   mes_itp_dstart=1
   mes_itp_type=128
!use string copy to move the data
   tt(0) = tp-1
   string(addr(mes_itp_data(1)))=string(addr(tt(0)))
   mes_itp_data(1) = 2;           !overwrite len with 'set pad params'
   mes_len = tp+1
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, am1 reply, stat
!
   ! always use control d
   am1 reply = 4; ! "condition y"
!
   cycle 
!
      m == control d_in buf
      if  m==null then  m == pop(control d_inp q) and  control d_in cnt = control d_in cnt-1
!
      if  m==null thenexit 
      !! terminate with "normal" (shouldnt happen)
!
      n = control d_in buf pos; ! start of block - control d_in buf pos = 0
!
      cycle 
         cycle 
            stat = l_rxs
!
            if  stat&xopl#0 start 
               control d_in buf == m; ! retain buffer for retry
               am1 reply = 64; ->am1 rep
            finish 
!
            if  stat&ready#0 start 
               !! l i m i t sent
               am1 reply = 2; ! long block
               control d_in buf pos = n
               control d_in buf == m; ! retain for later
               ->am1 rep
            finish 
!
            if  l_txs&ready#0 thenexit 
         repeat 
!
!
         if  n>m_a(0) start 
            free buffer(m)
            control d_in buf == null; control d_in buf pos = 0
!
            if  control d_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 
!
!
routine  mon mes(record  (mef) name  mes)
!-------------------------------
   integer  i, j, k, n
   record  (itpf) name  itp
!
   k = mes_len; itp == mes_itp
   write(k, 1); space; space
   j = 7
   write(itp_type,1)
   cycle  i = 1, 1, k-1
      n = itp_data(i)
      if  32<=n<=127 start 
         printsymbol(n);  j = j+1
      else 
         printsymbol('¬');  write(n,3); j=j+4
      finish 
      if  j>80 then  newline and  j=0
   repeat 
   newline; select output(0)
end 
!
!
routine  mon p(record  (pe) name  p)
!-------------------------------
   integer  i
   printstring(" fn ="); write(p_fn, 1)
   printstring(" ts port"); write(p_ts port, 1)
   printstring(" task port"); write(p_task port, 1)
   printstring(" a2"); write(p_a2, 1)
   ifnot  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 
!
!
endofprogram