! Common utility procedures for (Moose) INet process, GDMR, Jan. 1988

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

%include "INet:Formats.Inc"
%include "GDMR_H:DateTime.Inc"

%systemroutinespec phex(%integer i)

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

%externalintegerspec clock ticks

%systemintegerfnspec global heap get(%integer bytes)

%systemstring(127)%fnspec itos(%integer n, p)
%systemroutinespec phex2(%integer n)
%externalstring(15)%fnspec date
%externalstring(15)%fnspec time

%externalstring(3)%fn itos2(%integer what)
   %integer h, l
      h = what // 10;  l = what - 10 * h
      %result = to string(h + '0') . to string(l + '0')
%end

%externalstring(15)%fn xtos(%integer i)
   %string(15) s = ""
   %integer j, ch
      %for j = 1, 1, 8 %cycle
         ch = (i >> 28) & 15
         %if ch <= 9 %then s = s . to string(ch + '0') %c
                     %else s = s . to string(ch - 10 + 'A')
         i = i << 4
      %repeat
      %result = s
%end

%externalintegerfn generate timestamp
   %result = rem(get datestamp, 24 * 60 * 60) * 1000
%end

%externalstring(31)%fn convert timestamp(%integer stamp)
   %integer h, m, s, hu
      hu = stamp // 10
      s = hu // 100;  hu = hu - 100 * s
      m = s // 60;  s = s - 60 * m
      h = m // 60;  m = m - 60 * h
      %result = itos2(h) . ":" . itos2(m) . ":" . itos2(s) . "." . itos2(hu)
%end

%externalintegerfn msecs timestamp
   %result = clock ticks << 7 ! 1;  ! *128, near enough...
%end


! Buffer manipulation stuff.  Only the free-list stuff is defined here, as the
! en/dequeue procedures are merely aliased to the standard system ones.

%systemroutinespec enqueue buffer %alias "enqueue" %c
                                  (%record(buffer fm)%name b,
                                   %record(queue fm)%name q)
%systemrecord(buffer fm)%mapspec dequeue buffer %alias "dequeue" %c
                                                (%record(queue fm)%name q)

%externalrecord(queue fm)%spec buffer lookaside list

! NB: claim and release may be called from several autonomous processes, so
! use the system-supplied interlocked queuing routines.

%externalrecord(buffer fm)%map claim buffer
   %record(buffer fm)%name b
   %label L
      b == dequeue buffer(buffer lookaside list)
      %result == nil %if b == nil
      D0 = copy or clear // 4
      D1 = 0;  A0 = addr(b)
   L: *move.l D1, (A0)+
      *dbra D0, L
      %result == b
%end

%externalroutine release buffer(%record(buffer fm)%name b)
   enqueue buffer(b, buffer lookaside list)
%end

%externalroutine purge queue(%record(queue fm)%name q)
   %record(buffer fm)%name b
      %cycle
         b == dequeue buffer(q)
         %return %if b == nil
         release buffer(b)
      %repeat
%end

%externalroutine copy headers(%record(buffer fm)%name from, to)
   %label L
      D0 = copy or clear // 4
   L: *move.l (A0)+, (A1)+
      *dbra D0, L
      to_IP header  == record(addr(to) + (addr(from_IP header) - addr(from)))
      to_header 2   == record(addr(to) + (addr(from_header 2)  - addr(from)))
      to_data start == byteinteger(addr(to) + (addr(from_data start) - addr(from)))
%end

%externalroutine copy all(%record(buffer fm)%name from, to)
   %label L
      D0 = buffer size // 4
   L: *move.l (A0)+, (A1)+
      *dbra D0, L
      to_IP header  == record(addr(to) + (addr(from_IP header) - addr(from)))
      to_header 2   == record(addr(to) + (addr(from_header 2)  - addr(from)))
      to_data start == byteinteger(addr(to) + (addr(from_data start) - addr(from)))
%end


! "Named heap"

%externalrecord(*)%map named heap get(%integer bytes, %string(31) name)
   %integer x
   %label Z
      !! printstring("Named heap get: ");  printstring(name)
      !! space;  write(bytes, 0);  newline
      x = global heap get(bytes + 4)
      D1 = bytes // 4
      D0 = 0;  A0 = x
   Z: *move.l D0, (A0)+
      *dbra D1, Z
      FS insert(name, x)
      %result == record(x)
%end


! Network (re)ordering (not required for 680x0)
!
!%routine net order short(%shortname X)
!%end
!
!%routine net order long(%integername X)
!%end


! Checksum calculation

%externalintegerfn calculate checksum(%record(*)%name start, %integer bytes)
   %integer i, c = 0
      byteinteger(addr(start) + bytes) = 0 %if bytes & 1 # 0;  ! Odd, pad
      c = c + (shortinteger(i) & 16_FFFF) %c
         %for i = addr(start), 2, addr(start) + (bytes - 1) & (\ 1)
      c = (c & 16_FFFF) + (c >> 16);  ! Carries
      c = (c & 16_FFFF) + (c >> 16);  ! ... and again
      %result = c
%end

%externalintegerfn calculate pseudo checksum(%record(*)%name start,
                                             %integer bytes,
                                             %integer source, destination,
                                             %integer protocol, length)
   ! Calculates the pseudo-checksum for TCP & UDP, incorporating the
   ! source and destination, length and protocol type.  NOTE that this
   ! calculation must be done with the packet header and the pseudo-header
   ! fields in NETWORK order -- we switch the pseudo-header around in here.
   %integer i, c = 0
      byteinteger(addr(start) + bytes) = 0 %if bytes & 1 # 0;  ! Odd, pad
      c = c + (shortinteger(i) & 16_FFFF) %c
         %for i = addr(start), 2, addr(start) + ((bytes - 1) & (\ 1))
!N!   net order long(source);  net order long(destination)
!N!   net order short(shortinteger(addr(length)))
!N!   net order short(shortinteger(addr(protocol)))
      length = length & 16_FFFF;  ! Zap sign-extension 
      c = c + (source      & 16_FFFF) + source      >> 16
      c = c + (destination & 16_FFFF) + destination >> 16
      c = c + length + protocol
      c = (c & 16_FFFF) + (c >> 16);  ! Carries
      c = (c & 16_FFFF) + (c >> 16);  ! ... and again
      %result = c
%end


! INet address stuff

%externalroutine print INet address(%integer addr)
   write(addr >> 24 & 255, 0);  print symbol('.')
   write(addr >> 16 & 255, 0);  print symbol('.')
   write(addr >>  8 & 255, 0);  print symbol('.')
   write(addr       & 255, 0)
%end

%externalstring(15)%fn INet address to S(%integer addr)
   %result = itos(addr >> 24 & 255, 0) . "." . %c
             itos(addr >> 16 & 255, 0) . "." . %c
             itos(addr >>  8 & 255, 0) . "." . %c
             itos(addr       & 255, 0)
%end

%externalroutine split INet address(%integer address,
                                    %integername host, network, class)
   %if address & 16_80000000 = 0 %start
      ! Class A
      host = address & 16_00FFFFFF
      network = address & 16_FF000000
      class = 'A'
   %else %if address & 16_C0000000 = 16_80000000
      ! Class B
      host = address & 16_0000FFFF
      network = address & 16_FFFF0000
      class = 'B'
   %else %if address & 16_E0000000 = 16_C0000000
      ! Class C
      host = address & 16_000000FF
      network = address & 16_FFFFFF00
      class = 'C'
   %else
      ! Unknown
      host = address
      network = 0
      class = '@'
   %finish
%end


! Miscellaneous utility things

%externalroutine print ether address(%record(ether address fm)%name a)
   %integer i
      %for i = 0, 1, 5 %cycle
         print symbol('-') %unless i = 0
         phex2(a_x(i))
      %repeat
%end

%externalstring(23)%fn ether address to S(%record(ether address fm)%name e)
   %string(23) s
   %integer i, ch
      s = ""
      %for i = 0, 1, 5 %cycle
         s = s . "-" %if i # 0
         ch = (e_x(i) >> 4) & 15
         %if ch <= 9 %then ch = ch + '0' %else ch = ch - 10 + 'A'
         s = s . to string(ch)
         ch = (e_x(i)     ) & 15
         %if ch <= 9 %then ch = ch + '0' %else ch = ch - 10 + 'A'
         s = s . to string(ch)
      %repeat
      %result = s
%end

%externalroutine pdate
   printstring(date);  space
   printstring(time);  spaces(2)
%end

%externalintegerfn generate ISS
   %result = clock ticks << 4
%end


! Dump packet (hex & ASCII)

%systemroutinespec phexx2 %alias "phex2" (%integer i)

%externalroutine dump(%bytename buffer, %integer bytes)
   %integer i, j, k
      %return %if bytes <= 0
      newline
      i = 0
      %while i < bytes %cycle
         print symbol('[')
         write(i, -4)
         printstring("]  ")
         %for j = 0, 1, 15 %cycle
            %if i + j >= bytes %then space %and space %c
                               %else phexx2(byteinteger(addr(buffer) + j))
            space %if j & 3 = 3
         %repeat
         printstring(" |")
         %for j = 0, 1, 15 %cycle
            %if j + i < bytes %start
               k = byteinteger(addr(buffer) + j) & 127
               %if k = 0 %start
                  k = '.'
               %else %unless ' ' <= k <= '~'
                  k = '_'
               %finish
               printsymbol(k)
            %else
               space
            %finish
         %repeat
         printsymbol('|')
         newline
         i = i + 16;  buffer == buffer [16]
      %repeat
      newline
%end

%end %of %file
