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

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

! 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

%constinteger ARP lives = 4

%include "INet:Formats.Inc"
%include "INet:Utility.Inc"
%include "INet:Stats.Inc"
%include "INet:Control.Inc"

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

%externalintegerfnspec global heap get(%integer bytes)

%externalroutinespec FS insert(%string(31) what, %integer value)

%externalinteger control = 0
%conststring(31) control name = "INET_CONTROL"

%externalrecord(semaphore fm) dispatch semaphore = 0

%externalinteger max TCP = 0, max UDP = 0

%externalrecord(TCB table fm)%name TCB table
%externalrecord(UDP table fm)%name UDP table

%externalrecord(INet statistics fm) stats = 0

! Queue headers

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



! External module specs come here....

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

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

%externalroutinespec start ether(%integer station)
%externalroutinespec ether outbound(%record(buffer fm)%name b)
!%externalroutinespec ARP timer

%externalroutinespec define IP addresses(%integer x, fast)
%externalroutinespec IP inbound(%record(buffer fm)%name b)

%externalroutinespec TCP enable(%integer i)
%externalroutinespec TCP inbound(%record(buffer fm)%name b)
%externalroutinespec TCP outbound(%record(buffer fm)%name b)
%externalroutinespec TCP packet delivered(%record(buffer fm)%name b)
%externalroutinespec TCP fast timer
%externalroutinespec TCP slow timer

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

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

%externalroutinespec start routed
%externalroutinespec routed clock


! 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, no slow
      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
      *and.l #slow mask, D0               { Slow timer expired?
      *bne no slow                        { No, ->
      *move.l #1, slow timer              { Tell dispatcher
no slow:
      *move.l #1, fast timer              { Tell dispatcher
      int signal semaphore(dispatch semaphore)
      return from interrupt
      *move.l D0, D0;  ! HMD-proof it
%end


! Main program

%begin
   %record(buffer fm)%name b
   %integer status, i, k
      open input(3, ":N");  select input(3)
      open output(3, ":T");  select output(3)
      ! Initialise semaphores, queues
      setup semaphore(dispatch semaphore)
      setup queue(IP inbound queue)
      setup queue(TCP inbound queue)
      setup queue(TCP outbound queue)
      setup queue(TCP packet delivered queue)
      setup queue(UDP outbound 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
      ! Insert the name of our control word and statistics record
      FS insert(control name, addr(control))
      FS insert(INet statistics record name, addr(stats))
      ! Start up the user interface
      start user interfaces(max TCP, max UDP)
      %if max TCP = 0 %start
         printstring("TCP units not configured");  newline
      %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
         printstring("UDP units not configured");  newline
      %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
!S!   peer table == named heap get(peer table size, peer table name)
!S!   p == new peer info(-1);  ! Broadcast record
      ! Start devices, timers.
      start clock
      start routed
      start slow ether
      start ether(slow ether address)
!L!   lights and A(\ clear lights)
!L!   lights and B(\ clear lights)
      ! 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(IP inbound queue)
         %if b ## nil %start
            IP inbound(b)
            %continue
         %finish
         b == dequeue buffer(TCP inbound queue)
         %if b ## nil %start
            TCP inbound(b)
            %continue
         %finish
         b == dequeue buffer(TCP packet delivered queue)
         %if b ## nil %start
            TCP packet delivered(b)
            %continue
         %finish
         b == dequeue buffer(TCP outbound queue)
         %if b ## nil %start
            TCP outbound(b)
            %continue
         %finish
         %if fast timer # 0 %start
            fast timer = 0
            interface user timeouts
            TCP fast timer
         %finish
         b == dequeue buffer(UDP outbound queue)
         %if b ## nil %start
            UDP outbound(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
            TCP slow timer
            !ARP timer
            routed clock
         %finish
         semaphore wait(dispatch semaphore)
      %repeat
%end %of %program
