!*********************
!*                   *
!*   name server     *
!*    interface      *
!*                   *
!*********************
include  "deimosperm"


constrecord  (*) name  nil == 0


control  1


begin 

     conststring  (7)vsn = "vsn006b"



     recordformat  nscallf(integer  f, ( byteinteger  flags, address, c 
        integer  port, function code, string  (55) newname orc 
        string  (55) name) )

     recordformat  nsreplyf(integer  return code,(byteinteger  flags, address,c 
        integer  port, function code, string  (55) rest of name  orc 
        string  (55) myname) ) 

     recordformat  mef(record  (mef) name  link, byteinteger  len, c 
       type, integer  address,port,command,rport,      c 
          (record  (nsreplyf) nsreply                   orc 
           record  (nscallf) nscall) )



     recordformat  pe(byteinteger  ser, reply, fn, port,
       (integer  minport, maxport   or 
       record  (mef) name  mes, byteinteger  len, s1))



                                                  !transport service command codes


constinteger   open=x'6a00'
constinteger  sspreq=x'6c00'
constinteger   openack=x'6500'
constinteger   longblock=x'9000'
constinteger   longblockch=x'9000'
constinteger   longblockcs0=x'9400'
constinteger   singleshot=x'9800'





!                      incoming function codes
!                      -------------------------


!he following values may be added to the output codes

constinteger  release flag=x'80';           !release buffer at end of output
constinteger  tell flag=x'40';             !notify at end of transfer
constinteger  cs0flag=x'20'


constinteger  command mask=x'1f';           !to get command code

constinteger  enable port=0
constinteger  xdata=1
constinteger  ssout=2;            !single shot output
constinteger  xrdy=3
constinteger  xnotrdy=4
constinteger  xnodata=5
constinteger  xclose=6
constinteger  xreset=7
constinteger  xclosereq=8
constinteger  disable port=15
constinteger  output trace=16;     !force output of trace buffer


!                              outgoing function codes
!                                 -----------------------

constinteger  output done=0
constinteger  transfer error=1
constinteger  r input done=2
constinteger  input error=3
constinteger  ring down=4





!name server functions
!-----------------------

constinteger  lookup=0
constinteger  addname=1
constinteger  removename=2
constinteger  revlookup=3
constinteger  ownname=4


ownbyteintegerarray  portno(lookup:ownname)=1,5,6,2,4

     constinteger  request buffer = 0
     constinteger  release buffer = 1

                                   !********** various service numbers *************

   constinteger  name ser=15
    constinteger  ring ser = 10
     constinteger  buffer manager = 17
     constinteger  time int = 0

     constinteger  t3 ser = 21
                    !************************************************************
ownrecord  (pe) p


ownrecord  (pe) name  holdp

constinteger  pqmax=31;      !size of request queue
constinteger  names reply=0
constinteger  names fail=1

ownrecord  (pe) array  pq(0:pqmax)
owninteger  pqn,pqs,pqe;            !pqn=number of ps on queue
                                    !pqs=index of queue start
                                    !pqe=index of queue end


owninteger  reply port,state,t,c,i;               !state=idle or busy
                                    !t counts clock ticks while busy
                                    !c=repetion count

constinteger  timeout=2
constinteger  maxretries=3

constinteger  idle=0
constinteger  busy=1


constinteger  minrport=256;          !reply port limits
constinteger  maxrport=511

routinespec  from ring
routinespec  from clock
routinespec  from above
routinespec  sendup(integer  reply,fn,port)
routinespec  freebuffer(record  (mef) name  buf)
routinespec  start new
routinespec  do transfer
routinespec  finish req(integer  f)
routinespec   to ring(integer  f)
routinespec  swaba(integer  a,integer  l)


   pqn=0;  pqs=0;   pqe=0;    !init queue pointers

   linkin(name ser)
   i=map virt(buffer manager, 5, 4)
   i=map virt(buffer manager, 6, 5)
   
   state=idle
   

   !plug into reply ports
   p_minport=min rport
   p_maxport=max rport
   to ring(enable port)

reply port=min rport

alarm(50)

   cycle 
      p_ser=0;   poff(p)
      if  p_ser=own id start 
         if  p_reply=ring ser start 
            from ring
         else 
            from clock
         finish 
      else 
         from above
      finish 
   repeat 


integerfn  stoi(string  (*) name  s, integername  err)
!-----------------------------------------------------
!set err non-zero if error found
integer  n,c,i
   if  length(s)=0 then  ->error
   n=0
   for  i=1,1,length(s) cycle 
      c=charno(s, i)
      if  '0'<=c<='9' start 
         n=n*10+c-'0'
      finishelse  ->error
   repeat 
   result =n
error:
   err=1
   result =0
end 

routine  from ring;           !ignore all but input dones
switch  incmd(lookup:ownname)

   if  p_fn=r input done start 
      if  state=busy and  p_mes_port=reply port andc 
            p_mes_address=x'f0' and  (p_mes_command&x'ff00')=  c 
                 openack         start ;             !validate reply
            ->incmd(holdp_fn)
incmd(revlookup):
incmd(ownname):
            if  p_mes_len>2 start 
               swaba(addr(p_mes_nsreply_my name), p_mes_len-2)
               p_mes_len=length(p_mes_nsreply_my name)
            else 
               p_mes_len=0
               length(p_mes_nsreply_my name)=0
            finish 
            ->fin req

incmd(add name):
incmd(remove name):
            -> fin req
incmd(lookup):
            if  p_mes_len>5 start 
               swaba(addr(p_mes_nsreply_rest of name), p_mes_len-5)
            else 
               length(p_mes_nsreply_rest of name)=0
            finish 
            p_mes_len=length(p_mes_nsreply_rest of name)
fin req:
            free buffer(holdp_mes);   !free users buffer
            finish req(names reply)
            startnew
            return 
      finish 
      freebuffer(p_mes);     !ignore input
   finish 

end 



routine  from clock

   if  state=busy start 
      t=t+1
      if  t>timeout start 
         c=c+1
         if  c>maxretries start 
            free buffer(holdp_mes);     !release users buffer
            finish req(names fail)
            start new;         !if queue not empty
         else 
            do transfer;       !send it again
          finish 
      finish 
   finish 

   alarm(50)

end 


routine  from above

   if  pqn>=pqmax or  p_fn>own name start ;           !queue full
      free buffer(p_mes)
      sendup(p_reply, names fail, p_port)
   else 
      pq(pqs)=p
      pqs=(pqs+1) & pqmax
      pqn=pqn+1
   finish 
   if  state=idle then  start new

end 


routine  send up(integer  reply, fn, port)

   p_ser=reply;   p_reply=name ser
   p_fn=fn
   p_port=port
   pon(p)

end 


routine  freebuffer(record  (mef) name  buf)

record  (pe) p

   p_ser=buffer manager
   p_reply=own id
   p_mes==buf
   p_fn=release buffer
   pon(p)

end 



routine  start new

record  (mef) name  buf
integer  l,port,f, term, fac no, err
string  (63) ad1, ad2
switch  cmd(lookup:ownname)

restart:
   if  pqn>0 start 
       holdp==pq(pqe)
      c=1
      buf==holdp_mes
      buf_address=x'f0'
      buf_command=sspreq
      buf_rport=reply port
      buf_nscall_f=0
      ->cmd(holdp_fn)
cmd(lookup):
!see if name of form NnnTnn.Fnn
      if  buf_nscall_name -> ("N0T").ad1 and  ad1 -> ad1.(".F").ad2 start 
         err = 0;   !set by stoi if non-numeric string
         term=stoi(ad1, err)
         fac no=stoi(ad2, err)
         if  err = 0 start 
            buf_nsreply_return code = 0;   !return code
            buf_nsreply_flags = 0;         ! not set at moment
            buf_nsreply_address = term;    !address
            buf_nsreply_port=1;             !default for bsp opens
            buf_nsreply_function code=fac no
            buf_nsreply_restofname=""
            buf_len=0
            p_mes==buf

            finish req(names reply)
            ->restart;                      !avoid recursive call
         finish 
      finish 
cmd(remove name):
      l=(length(buf_nscall_name)+2) >> 1
      swaba(addr(buf_nscall_name), l)
      -> send
cmd(add name):
      l=(length(buf_nscall_newname) + 2) >> 1
      swaba(addr(buf_nscall_newname), l)
      l=l+3
      ->send
cmd(rev lookup):
      l=1
      ->send
cmd(own name):
      l=0
      ->send
send:
      buf_len=l+2
      buf_port=port no(holdp_fn)

      do transfer
   finish 

end 




routine  do transfer

   state=busy;   t=0
   p_mes==holdp_mes
   to ring(xdata)

end 


routine  finish req(integer  f)
                                         !transfer finished-remove from
                                         !queue and reply
   state=idle
   pqe=(pqe+1)&pqmax
   pqn=pqn-1
   reply port=reply port+1
   if  reply port>max rport then  reply port=min rport
   send up(holdp_reply, f, holdp_port)


end 



routine  to ring(integer  f)

   p_ser=ring ser;  p_reply=own id
   p_fn=f
   pon(p)

end 



routine  swaba(integer  address,integer  count)
!------------------------------------
!%integer i

!swab the bytes in the count words addressed by addr

!   %for i=1,1,count %cycle
!      address=((address>>8) & 16_ff)+(address<<8)
!      address==integer(addr(address)+2)
!   %repeat

   *=8_000321;   !loop: swab (r1)+
   *=8_005300;   !      dec  r0
   *=8_003375;   !      bgt  loop


end 
endofprogram