! INet process for interim Mouse (Moose), GDMR, Jan... 1988.  Based on
! VAX/VMS version.

%option "-NonStandard-Low-NoCheck-NoDiag-NoLine"

! This file contains some basic definitions and utility routines, together
! with the dispatcher loop at the end, but all the main components are
! linked in from separate files for convenience.  Note: (a) there is a clock
! interrupt handler to deal with timestamp calculations and timeouts; (b)
! there is one common semaphore which is waited for by the main dispatch loop;
! (c) most of the subsidiary files are common with the VMS version, except that
! network-ordering is a no-op for 680x0 processors; (d) HMD's compiler is unable
! to cope with real programs using more than a small number of variables.

%constinteger buffer pool size = 64

%externalinteger our address = 0
%externalinteger our network = 0

%constinteger ARP lives = 4

%include "INet:Common_formats.Inc"
%include "INet:Utility.Inc"

%systemroutinespec phex(%integer what)
%systemintegerfnspec free store

%externalintegerfnspec global heap get(%integer bytes)

%externalrecord(semaphore fm) dispatch semaphore = 0

%externalinteger max TCP = 0, max UDP = 0

%externalrecord(non peer info fm)%name sp
%externalrecord(TCB table fm)%name TCB table
%externalrecord(UDP table fm)%name UDP table
%externalrecord(peer table fm)%name peer table


! Queue headers

%externalrecord(queue fm) ether outbound         queue = 0
%externalrecord(queue fm) IP inbound             queue = 0
%externalrecord(queue fm) IP outbound            queue = 0
%externalrecord(queue fm) TCP inbound            queue = 0
%externalrecord(queue fm) TCP outbound           queue = 0
%externalrecord(queue fm) TCP first              queue = 0
%externalrecord(queue fm) TCP sixth              queue = 0
%externalrecord(queue fm) TCP packet delivered   queue = 0
%externalrecord(queue fm) UDP inbound            queue = 0
%externalrecord(queue fm) UDP outbound           queue = 0
%externalrecord(queue fm) ICMP inbound           queue = 0
%externalrecord(queue fm) ICMP outbound          queue = 0
%externalrecord(queue fm) buffer lookaside        list = 0



! External module specs (and dummies) come here....

%externalinteger activity count = 0

%externalroutinespec start user interfaces(%integername a, b)

%externalintegerspec slow ether address
%externalroutinespec start slow ether
%externalroutinespec slow ether outbound(%record(buffer fm)%name b)

%externalroutinespec IP inbound(%record(buffer fm)%name b)
%externalroutinespec IP outbound(%record(buffer fm)%name b)

%externalroutinespec TCP enable(%integer i)
%externalroutinespec TCP packet delivered(%record(buffer fm)%name b)
%externalroutinespec TCP sixth(%record(buffer fm)%name b)
%externalroutinespec TCP first(%record(buffer fm)%name b)
%externalroutinespec TCP inbound(%record(buffer fm)%name b)
%externalroutinespec TCP outbound(%record(buffer fm)%name b)
%externalpredicatespec TCP retransmit timeouts
%externalpredicatespec TCP pending acks
%externalroutinespec TCB time wait timeouts

%externalroutinespec UDP enable(%integer i)
%externalroutinespec UDP inbound(%record(buffer fm)%name b)
%externalroutinespec UDP outbound(%record(buffer fm)%name b)

%externalroutinespec ICMP inbound(%record(buffer fm)%name b)
%externalroutinespec ICMP outbound(%record(buffer fm)%name b)


! Clock handler

%owninteger slow timer = 0
%owninteger fast timer = 0

%constinteger clock priority = 6

%externalinteger clock ticks = 0

%constinteger slow mask = 127;  ! Deciseconds

%routine start clock
   %ownrecord(interrupt handler fm) handler = 0
   %label clock handler, C1, C2, C3
      setup interrupt handler(handler, addr(clock handler))
      add interrupt handler(handler, clock priority)
      !! printstring("INet module's clock handler inserted");  newline
      %return

clock handler:
      *move.l clock ticks, D0             { Get current time
      *addq.l #1, D0                      { Bump it
      *move.l D0, clock ticks             { Write it back
      *clr.l D1                           { Clear signal flag
      *tst.l activity count               { Anything doing
      *bls C1                             { Nothing fast, ->
      *move.l #1, fast timer              { Tell dispatcher
      *move.l #1, D1                      { Note we've to signal
C1:   *and.l #slow mask, D0               { Slow timer expired?
      *bne C2                             { No, ->
      *move.l #1, slow timer              { Tell dispatcher
      *move.l #1, D1                      { Note we've to signal
C2:   *tst.l D1                           { Signal required?
      *beq C3                             { No, ->
      int signal semaphore(dispatch semaphore)
C3:   return from interrupt
      *move.l D0, D0;  ! HMD-proof it
%end


! Main program

%begin
   %string(127) message, node name = "Unknown"
   %record(peer info fm)%name p
   %record(buffer fm)%name b
   %integer status, i, k
      open input(3, ":N");  select input(3)
      open output(3, ":T");  select output(3)
      !our address = INet name to address(node name)
      !%if our address = 0 %start
      !   message = "Undefined INet address for '" . node name . "'"
      !   tell network operator(message)
      !   %stop
      !%finish
      !message = "INet address for '" . node name . "' is " . %c
      !          INet address to S(our address) . " ("
      !split INet address(our address, i, our network, k)
      !%if k = '@' %start
      !   message = message . "unknown network type)"
      !   tell network operator(message)
      !   %stop
      !%finish
      !message = message . to string(k) . ")";  ! Append address-class
      !tell network operator(message)
      sp == named heap get(non peer size, non peer name)
      ! Initialise semaphores, queues
      setup semaphore(dispatch semaphore)
      setup queue(ether outbound queue)
      setup queue(IP inbound queue)
      setup queue(IP outbound queue)
      setup queue(TCP inbound queue)
      setup queue(TCP outbound queue)
      setup queue(TCP first queue)
      setup queue(TCP sixth queue)
      setup queue(TCP packet delivered queue)
      setup queue(UDP inbound queue)
      setup queue(UDP outbound queue)
      setup queue(ICMP inbound queue)
      setup queue(ICMP outbound queue)
      setup queue(buffer lookaside list)
      !! printstring("Dispatch semaphore at ")
      !! phex(addr(dispatch semaphore));  newline
      !! printstring("Free list at ");  phex(addr(buffer lookaside list))
      !! newline
      ! Start up the user interface
      start user interfaces(max TCP, max UDP)
      %if max TCP = 0 %start
         tell network operator("TCP units not configured")
      %else
         max TCP = max TCB table %if max TCP > max TCB table
         TCB table == named heap get(TCB size * max TCP, TCB table name)
         TCP enable(i) %for i = 1, 1, max TCP
      %finish
      %if max UDP = 0 %start
         tell network operator("UDP units not configured")
      %else
         max UDP = UDP table entries %if max UDP > UDP table entries
         UDP table == named heap get(UDP entry size * max UDP, UDP table name)
         UDP enable(i) %for i = 1, 1, max UDP
      %finish
      ! Set up buffer pool, etc
      %for i = 1, 1, buffer pool size %cycle
         b == record(global heap get(buffer size + 4))
         release buffer(b)
      %repeat
      peer table == named heap get(peer table size, peer table name)
      ! Start devices, timers
      start clock
      start slow ether
      our address = 193 << 24 ! 208 << 16 ! 205 << 8 ! slow ether address
      our network = our address & 16_FFFFFF00
      p == new peer info(our network);  !<<<<<<<< Broadcast record <<<<<<<<
      {} printstring("Slow ether INet address: ")
      {} print inet address(our address);  newline
      !setup gateways
      !start ICMP mailbox
      ! And off we go....  Scan the queues in turn, looking for buffers to
      ! dequeue.  If we find any we call the appropriate service routine, then
      ! go back up to the top of the loop
      printstring("INet: ");  write(free store, 0)
      printstring(" free");  newline
      %cycle
         b == dequeue buffer(ether outbound queue)
         %if b ## nil %start
            slow ether outbound(b)
            %continue
         %finish
         b == dequeue buffer(IP inbound queue)
         %if b ## nil %start
            IP inbound(b)
            %continue
         %finish
         b == dequeue buffer(IP outbound queue)
         %if b ## nil %start
            IP outbound(b)
            %continue
         %finish
         ! NB: TCP processing MUST come AFTER ether & IP to avoid
         ! race conditions with retransmit queues.  Note also that TCP
         ! processing must take place in such an order as to ensire that
         ! later stages have a higer precedence than earlier stages -- this
         ! helps to avoid bogus "send-aheads".
         b == dequeue buffer(TCP packet delivered queue)
         %if b ## nil %start
            TCP packet delivered(b)
            %continue
         %finish
         b == dequeue buffer(TCP sixth queue)
         %if b ## nil %start
            TCP sixth(b)
            %continue
         %finish
         b == dequeue buffer(TCP first queue)
         %if b ## nil %start
            TCP first(b)
            %continue
         %finish
         b == dequeue buffer(TCP inbound queue)
         %if b ## nil %start
            TCP inbound(b)
            %continue
         %finish
         b == dequeue buffer(TCP outbound queue)
         %if b ## nil %start
            TCP outbound(b)
            %continue
         %finish
         %continue %if TCP pending acks
         %if fast timer # 0 %start
            !! printstring("Fast timer: ");  write(activity count, 0);  newline
            fast timer = 0
            %continue %if TCP retransmit timeouts
         %finish
         b == dequeue buffer(UDP inbound queue)
         %if b ## nil %start
            UDP inbound(b)
            %continue
         %finish
         b == dequeue buffer(UDP outbound queue)
         %if b ## nil %start
            UDP outbound(b)
            %continue
         %finish
         b == dequeue buffer(ICMP inbound queue)
         %if b ## nil %start
            ICMP inbound(b)
            %continue
         %finish
         b == dequeue buffer(ICMP outbound queue)
         %if b ## nil %start
            ICMP outbound(b)
            %continue
         %finish
         %if slow timer # 0 %start
            !! printstring("Slow timer");  newline
            slow timer = 0
            TCB time wait timeouts
            !? %continue
         %finish
         semaphore wait(dispatch semaphore)
      %repeat
%end %of %program
