%include "i:fs.inc"
%include "i:util.inc"

%begin; !Ethernet monitor station (Aug 83)

%bytespec(16_7fffc)status
%bytespec(16_7fffd)data
%bytespec(16_7ffff)control
%constinteger tbe=8,rd=4,rc=2,rbf=1

%integer sym,fileend,i
%integer phase=0,count=0
%constinteger filebeg=16_1000

%recordformat intf(%integer top,bot,put,get)
%record(intf)intbuf
%constinteger bufsize=16384
%bytearray buffer(0:bufsize-1)

%constinteger cr=13,esc=27,nopage=8
%integer crc=0,col=0,dov=0,fov=0,pak=0,ack=0,ret=0,qqq=0

%routine preload; !Read Z80 object file into an array
%integer sym
  %onevent 9 %start
    closeinput; selectinput(0); %return
  %finish
  selectinput(1)
  fileend = filebeg
  %cycle
    readsymbol(sym); buffer(fileend) = sym; fileend = fileend+1
  %repeat
%end

%routine load; !Load Station's Z80 (cutting filestore umbilical)
%integer sym,ad
  sym = 0; status = sym; ! Disable station interrupts
  control = 16_03
  %cycle
  %repeatuntil status&tbe#0
  ad = 16_1000
  %while ad<fileend %cycle
    sym = buffer(ad)
    data = sym
    %cycle
    %repeatuntil status&tbe#0
    ad = ad+1
  %repeat
  control = 16_0b
  %cycle
  %repeatuntil status&tbe#0
%end

%routine ether interrupt handler(%record(*)%name r)
*=16_43FA;*=16_000C;          !   lea int,a1
*=16_21C9;*=16_1070;          !   move.l a1,$1070
*=16_2348;*=16_0006;          !   move.l a0,6(a1)
*=16_4E75;                    !   rts
*=16_48E7;*=16_C0C0;          ! int movem.l d0/d1/a0/a1,-(sp)
*=16_207C;*=16_0006;*=16_00A1;!   move.l #$000600a1,a0; !Overwriten with A0
*=16_1039;*=16_0007;*=16_FFFC;!   move.b $7fffc,d0
*=16_0800;*=16_0001;          !   btst #1,d0
*=16_6734;                    !   beq.s data
*=16_2268;*=16_0008;          !   move.l 8(a0),a1
*=16_720E;                    !   moveq #14,d1
*=16_12F9;*=16_0007;*=16_FFFF;!   move.b $7ffff,(a1)+
*=16_1039;*=16_0007;*=16_FFFC;! loop move.b $7fffc,d0
*=16_0800;*=16_0002;          !   btst #2,d0
*=16_67F4;                    !   beq loop
*=16_12F9;*=16_0007;*=16_FFFD;!   move.b $7fffd,(a1)+
*=16_51C9;*=16_FFEC;          !   dbra d1,loop
*=16_B3E8;*=16_0004;          !   cmp.l 4(a0),a1
*=16_6602;                    !   bne.s x1
*=16_2250;                    !   move.l (a0),a1
*=16_2149;*=16_0008;          ! x1 move.l a1,8(a0)
*=16_4CDF;*=16_0303;          ! intret movem.l (sp)+,d0/d1/a0/a1
*=16_4E73;                    !   rte
*=16_1039;*=16_0007;*=16_FFFD;! data move.b $7fffd,d0
*=16_60F2;                    !   bra intret
%end

%routine date time
%string(31)s
  length(s) = fcommr('G'<<8,"",charno(s,1),31)
  printstring(s)
%end

%routine at(%integer line,col)
  printsymbol(esc); printsymbol('Y')
  printsymbol(line+' '); printsymbol(col+' ')
%end

%routine log(%integername err)
  err = err+1
  %if err=1 %start
        %if err==pak %then at(1,40) %and printstring("Pak=") %c
    %elseif err==ack %then at(1,50) %and printstring("Ack=") %c
    %elseif err==crc %then at(1, 0) %and printstring("CRC=") %c
    %elseif err==col %then at(1,10) %and printstring("COL=") %c
    %elseif err==dov %then at(1,20) %and printstring("DOV=") %c
    %elseif err==fov %then at(1,30) %and printstring("FOV=") %c
    %elseif err==ret %then at(1,60) %and printstring("Ret=") %c
                     %else at(1,70) %and printstring("???=")
  %finish
      %if err==pak %then at(1,44) %c
  %elseif err==ack %then at(1,54) %c
  %elseif err==crc %then at(1, 4) %c
  %elseif err==col %then at(1,14) %c
  %elseif err==dov %then at(1,24) %c
  %elseif err==fov %then at(1,34) %c
  %elseif err==ret %then at(1,64) %c
                   %else at(1,74)
  phex4(err)
%end

%recordformat rawf(%byte st,ds,dp,ss,sp,ty,sq,dsl,dsh,psl,psh,d1,d2,d3,d4,d5)
%record(rawf)%name raw
%recordformat xf(%short row,col,from,to, %c
                 data,redata,dsq,size,%string(5)content, %c
                 %short acks,reacks,asq)
%constinteger maxx=40
%record(xf)%array x(1:maxx)
%record(xf)%name xx

%routine idle
%integer n = intbuf_put-intbuf_get
  n = n+intbuf_bot-intbuf_top %if n<0
  at(0,35); phex4(n>>4)
%end

%routine process packet
%owninteger nextslot=0
%integer i,j,from,to,slot
  idle
  %if raw_st&2#0 %start
    log(dov); %return
  %finish
  %if raw_st&1#0 %start
    log(fov); %return
  %finish
  log(crc) %if raw_st&8#0
  log(col) %if raw_st&4#0
  from = raw_ss<<8+raw_sp
  to = raw_ds<<8+raw_dp
! Decide which slot to use.
  %if from&16_7f00=16_7000 %and to&16_7000=16_1000 %start
    slot = (from&15)<<1
  %elseif from&16_7000=16_1000 %and to&16_7f00=16_7000 %start
    slot = (from>>8&15)<<1-1
  %elsestart
    log(qqq)
    nextslot = rem(nextslot+1,maxx-30)
    slot = maxx-nextslot
  %finish
  xx == x(slot)
!found:
  xx_from = from; xx_to = to
  %if raw_ty&128=0 %start
    log(pak)
    %if raw_sq=0 %or raw_sq#xx_dsq %start
      xx_dsq = raw_sq; xx_data = xx_data+1
    %else
      xx_redata = xx_redata+1; log(ret)
    %finish
    xx_size = raw_psl+raw_psh<<8
    i = raw_dsl+raw_dsh<<8; xx_size = \i %unless i=xx_size
!   length(xx_content) = 5
!   %for i=1,1,5 %cycle
!     j = byteinteger(addr(raw_d1)+i-1)
!     j = '_' %unless ' '<=j<=126
!     charno(xx_content,i) = j
!   %repeat
    at(xx_row,xx_col); phex4(xx_from); phex4(xx_to)
    space; phex4(xx_data); space; phex4(xx_redata); space
    phex2(xx_dsq); space; phex4(xx_size-14)
!   space; printstring(xx_content)
  %else
    log(ack)
    %if raw_sq=0 %or raw_sq#xx_asq %start
      xx_asq = raw_sq; xx_acks = xx_acks+1
    %else
      xx_reacks = xx_reacks+1; log(ret)
    %finish
    at(xx_row,xx_col); phex4(xx_from); phex4(xx_to)
    at(xx_row,xx_col+27)
    phex4(xx_acks); space; phex4(xx_reacks); space; phex2(xx_asq)
  %finish
%end

!*   I N I T I A L I S E

  set terminal mode(nopage)
  intbuf_top = addr(buffer(0))
  intbuf_bot = intbuf_top+bufsize
  intbuf_put = intbuf_top; intbuf_get = intbuf_top
  openinput(1,"ether:monitor.bin"); preload
  printsymbol(esc); printsymbol('v')
  printstring("Ethernet Monitor Station")
  printstring("                             Up since ")
  date time; newline
  load
  ether interrupt handler(intbuf)
  status = 6; ! Re-enable station interrupts (receive-only)
  at(2,0);  printstring("From To  Data (re) No Size Acks (re) No")
  at(2,40); printstring("From To  Data (re) No Size Acks (re) No")
  %for i = 1,1,maxx %cycle
    xx == x(i); xx = 0
    %if i<=maxx>>1 %start
      xx_row = i+2; xx_col = 0
    %else
      xx_row = i+2-maxx>>1; xx_col = 40
      at(xx_row,39); printsymbol('|')
    %finish
  %repeat

!** Main Program Loop

%cycle
  %while intbuf_get#intbuf_put %cycle; ! Update Loop
    raw == record(intbuf_get)
    intbuf_get = intbuf_get+16
    intbuf_get = intbuf_top %if intbuf_get=intbuf_bot
    process packet
  %repeat
  idle %while intbuf_get=intbuf_put
%repeat

%endofprogram
