! 2MHz ether process for new Mouse. 
! AJS 12/08/87

%Option "-low-nons-nocheck-nodiag-noline-nostack"
%Include "mouse.inc"
{}@16_41c %routine printsymbol(%integer x)
{}@16_420 %routine printstring(%string(255)s)

%Constinteger min = 0, max = 532, min0 = 6, max0 = 538
%Constinteger dtx=16_30, stx=16_20, rdy=16_10, nak=16_50, etx=16_0B,
              opn=16_80, cls=16_90, ack=16_C0, esa=16_05, ssa=16_07, err=16_A0

%Constinteger ion  = 2_110
%Constinteger ioff = 2_0

%constinteger port free    = 1,
              port owned   = 2
%Constinteger port closed  = 1,
              port opening = 2,
              port open    = 3,
              port closing = 4
%constinteger await RDY    = 1,
              await ACK    = 2,
              await DTX    = 1,
              await ETX    = 2,
              await NAK    = 3,
              pending DTX  = 4

%constinteger  Success           =  0,
               Undefined Error   =  1,
               Bad Function Code =  2,
               Bad Port Number   =  3,
               Port not owned    =  4,
               No Free Port      =  5,
               Port already owned=  6,
               Packet was NAKed  =  7,
               Port was closed   =  8,
               Port was open     =  9,
               Bad Transmit length=10,
               Abort             = 11

%Constinteger Max Ctrl = 128  {Size of ether event pool from station}
%Constinteger Max Port = 31   {Last Port in station}

! Format of request packet from client process (mapped onto data field of msg)
%Recordformat reqfm ((%Byte code %or %byte result),
                     %Byte port,
                     (%Byte rdte %or %Byte port0mode),
                     (%Byte rsap %or %Byte channel),
                     (%Integer length %OrInteger maxbytes %orinteger resbytes),
                     %bytearray b(0:532))

! Port record
! OPEN  = port closed, port opening, port open, port closing
! TMODE = 0, await RDY, await ACK
! RMODE = 0, pending DTX, await DTX, await ETX
! OWNED = port free, port owned
%Recordformat PortFm (%Byte open,tmode,rmode,lsap,owned,rdte,rsap,h,
                      %Record(queuefm) tpend,rpend,
                      %Record(messagefm)%name tcurr,rcurr,mgmt,
                      %Integer inbuff,lin,outbuff,lout,resbytes)

! Ether Station registers
@16_7fffc %Byteinteger Eths, Ethd, Ethz, Ethc

%Ownrecord(interrupthandlerfm)   ih {must be first %OWN}
%Ownrecord(mailboxfm)%name       mbox
%Ownrecord(semaphorefm)%name     eevent
%Ownrecord(Portfm)%array         Ports (0:MaxPort) = 0(*)
! ether event pool:
%owninteger                      bs, be, in, out
%ownbytearray                    port,type(0:maxctrl-1)
%Ownbyte                         LDTE=0
!!@16_3730 %integer olderr,olddtx,*{oldrdy},*{oldstx},oldack,oldnak

%Routine Wed (%Integer x)
   eths = ioff
   %Cycle; %Repeatuntil eths&8#0; ethd=x
   eths = ion
%End

%Routine Wec (%Integer x)
   eths = ioff
   %Cycle; %Repeatuntil eths&8#0; ethc=x
   eths = ion
%End

%Routine Setup Interrupts
%integer x
@0(a0)%Record(portfm) p
!??@0(a1)%Record(reqfm) r
%label int, done
%label er3,er35,er4,er5
   bs  = addr(port(0))
   be  = addr(port(MaxCtrl-1))+1
   in  = bs
   out = bs
   ih = 0
   ih_pc = addr(int)
   x = {or}move to sr (16_2000)
   Add Interrupt Handler (4)
   x = move to sr (x)
   eths = ion
   %Return
 
int:
%Register (d0) %integer  ps code
%Register (d0) %integer  stat
%Register (d1) %integer  port
%Register (d1) %integer  nps code
%Register (a0) %integer  q

   *temp 0
   stat = eths
   -> done %unless stat & 128 # 0
   %if stat & 2 = 0 %start
      ! Must be data - ignore. Maybe we should flag this as an error
      stat = ethd
      *temp d0/a0
{     olderr = olderr+1
      printsymbol(7)
      Printstring("Ether: data int");printsymbol(nl)
      -> done
   %finish
   nps code = ethc
   ps code = nps code & 16_F0
   %if ps code # 0 %start     { Port Specific code }
      %cycle; %repeatuntil eths & 1 # 0
      port = ethd & 31
      %if ps code = stx %start
         eths = ioff
         *movem.l d1-d2/a1-a3,-(sp)
         *temp d0
         a0 = addr(ports(port))
         *temp 0
         *move.l a0,-(sp)
         d1 = p_lin
         a0 = p_inbuff
         *lea eths, a2
         *lea ethd, a3
         *moveq #-1, d0
er3:
         *subq    #1,d1
         *bmi     er4
er35:
         *move.b  (a2),d2
         *btst    #0,d2
         *beq     er35
         *move.b  (a2),d2
         *addq.l  #1,d0
         *btst    #1,d2
         *bne     er5
         *move.b  (a3),(a0)+
         *bra     er3
er4:                    ;! Now discard rest
         *move.b  (a2),d2
         *btst    #0,d2
         *beq     er4
         *move.b  (a2),d2
         *addq.l  #1,d0
         *btst    #1,d2
         *bne     er5
         *move.b  (a3),d2
         *bra     er4
er5:
         *move.l  (sp)+,a0
         *movem.l (sp)+,d1-d2/a1-a3
         p_resbytes = d0
         eths = ion
         pscode = etx
      %elseif pscode = rdy 
         *temp d0
         a0 = addr(ports(port))
         *temp 0
         %if p_tmode = await RDY %start
            %unless p_open = port closing %start
               *movem.l d0-d1/a0-a1,-(sp)
               *temp d0
               wec (stx); wed (p_lsap)
               -> ew3 %if p_lout <= 0
               d1 = p_lout
               a0 = p_outbuff
ew2:
               *btst #3,eths
               *beq  ew2
               *move.b (a0)+, ethd
               *subq #1,d1
               *bgt  ew2
ew3:
               wec(etx)
               *temp 0
               *movem.l (sp)+, d0-d1/a0-a1
               p_tmode = await ACK
               -> done
            %finish
         %finish
         pscode = rdy {restore}
      %elseif pscode = dtx
         *temp d0
         a0 = addr(ports(port))
         *temp 0
         %if p_rmode = await DTX %start
            *temp d0
            wec (rdy); wed (p_lsap)
            *temp 0
            p_rmode = await ETX
            -> done
         %elseif p_open = port open %and p_rmode = 0
            p_rmode = pending DTX
            pscode = 1<<port
{           olddtx = olddtx!pscode
            -> done
         %finish
         pscode = dtx {restore}
      %Elseif pscode = ack
         pscode = 1<<port
{        oldack = oldack!pscode
{        oldnak = oldnak!pscode
{        oldnak = oldnak!!pscode
         pscode = ack
      %Elseif pscode = nak
         pscode = 1<<port
{        oldnak = oldnak!pscode
{        oldack = oldack!pscode
         pscode = nak
!     %Elseif pscode = err
      %else
{        olderr = olderr+1
         *temp d0/a0
         printstring("Ether: bad control character");printsymbol(nl)
         *temp 0
         -> done
      %finish
      q = in
      byteinteger (q) = port
      byteinteger (q+maxctrl) = pscode
      q = q + 1
      q = bs %if q = be
      %if q # out %start
         in = q
         *temp a0
         int signal semaphore (eevent)
         *temp 0
      %else
         *temp d0/a0
         printstring("Ether overflow");printsymbol(nl)
         *temp 0
         ! Overflow. Complain ?
      %finish     
   %else    { Non Port-specific code }
      %if nps code = ssa %start
         %cycle; %repeatuntil eths & 1 # 0
         ldte = ethd
      %else
         ! Non recognised control character. Flag as error ?
      %finish
   %finish

*temp
done:
   return from interrupt
   *nop
%End

%Routine Reply to Client (%Record(messagefm)%name msg)
   Send Message (msg,msg_reply)
%End

%Predicate Check Port Number (%Record(messagefm)%name msg)
%Record(reqfm)%name r == record(addr(msg_data(1)))
   %Trueif 0 <= r_port <= max port
   r_result = bad port number
   Reply to Client (msg)
   %false
%End

%Predicate Check Port Owned (%Record(messagefm)%name msg)
%Record(reqfm)%name r == record(addr(msg_data(1)))
   %Trueif ports(r_port)_owned = port owned
   r_result = port not owned
   Reply to Client (msg)
   %False
%End

%Predicate Check Port Status (%Record(messagefm)%name msg, %integer expect)
! fail and %False if expect # port status
%Record(reqfm)%name r == record(addr(msg_data(1)))

   %Routine Do Fail (%Integer fail)
      r_result = fail
      Reply to Client (msg)
   %End      

   %if ports(r_port)_open = port open %start
      do fail (port was open) %andfalseif expect = port closed
   %Else            
      do fail (port was closed) %andfalseif expect = port open
   %Finish
   %True
%End

%Routine Bounce Queue (%record(queuefm)%name q)
%Record(reqfm)%name r
%Record(messagefm)%name msg
   msg == dequeue (q)
   %while msg ## NIL %cycle
      r == record(addr(msg_data(1)))
      r_result = abort
      Reply To Client (msg)
      msg == dequeue (q)
   %Repeat
%End

%Routine Claim Port (%Record(messagefm)%Name msg)
%Record(reqfm)%name r == record(addr(msg_data(1)))
%Integer i

   %Routine Allocate (%Integer p)
      %If ports(p)_owned = port free %Start
         ports(p)_owned = port owned
         r_result = success
         r_port = p
      %Else
         r_result = port already owned
      %Finish
      Reply To Client (msg)
   %End

   %if r_port = 255 %start
      %For i = 1, 1, Max Port %Cycle
         %If ports(i)_owned = port free %Start
            Allocate (i)
            %Return
         %Finish
      %Repeat
      r_result = no free port
      Reply to Client (msg)
   %Else
      %returnunless check port number(msg) 
      Allocate (r_port)
   %Finish
%End

%Routine Free Port (%Record(messagefm)%Name msg)
%Record(reqfm)%name r == record(addr(msg_data(1)))
   %Returnunless check port number (msg)
   %Returnunless check port owned  (msg)
   %unless r_port = 0 %start
      %Returnunless check port status (msg,port closed)
   %finish
   ports(r_port)_Owned = port free
   r_result = success
   Reply to Client (msg)
%End

%Routine Open Port (%Record(messagefm)%name msg, %Integer state)
%Record(portfm)%name p
%Record(reqfm)%name r == record(addr(msg_data(1)))
%switch sw(0:1)
   p == ports(r_port)
   -> sw(state)

sw(0):   ;! User Request In
   %if r_port = 0 %start      ;! Null Operation
      r_result = success
      Reply to Client (msg)
      %Return
   %Finish
   %Returnunless check port number (msg)
   %Returnunless check port owned (msg)
   %Returnunless check port status (msg,port closed)

   wec(opn); wed(p_lsap)
   p_mgmt == msg
   p_open = port opening
   %Return

sw(await RDY):   ;! RDY received
   wec(stx)
   wed(p_lsap); wed(r_rdte); wed(r_rsap)
   wec(etx)
   p_tcurr == NIL; p_rcurr == NIL; p_mgmt == NIL
   p_tmode = 0; p_rmode = 0
   setup queue (p_tpend)
   setup queue (p_rpend)
   p_open = port open
   p_rdte = r_rdte; p_rsap = r_rsap
   r_result = success
   Reply to Client (msg)
%End

%Routine Close Port (%Record(messagefm)%name msg, %integer state)
%Record(reqfm)%name r == record(addr(msg_data(1)))
%Record(reqfm)%name tr
%Record(portfm)%Name p
%switch sw(0:2)

   p == ports(r_port)
   -> sw(state)

sw(0):   ;! User request in
   %returnunless check port number (msg)
   %Returnunless check port status (msg,port open)

   p_mgmt == msg
   p_open = port closing
   Bounce queue (p_tpend)
   Bounce queue (p_rpend)
   %Returnif p_rmode = await ETX

sw(await ETX):  ;! the STX arrived or wasn't awaited

   %unless p_rcurr == NIL %start
      tr == record(addr(p_rcurr_data(1)))
      tr_result = abort
      Reply To Client (p_rcurr)
      p_rcurr == NIL
   %finish
   p_rmode = 0
   %Returnif p_tmode = await RDY

sw(await RDY):

   %unless p_tcurr == NIL %start
      tr == record(addr(p_tcurr_data(1)))
      tr_result = abort
      Reply To Client (p_tcurr)
      p_tcurr == NIL
   %finish
   p_tmode = 0 %unless p_tmode = await ACK
   wec (cls); wed(p_lsap)
   r_result = success
   Reply to Client (msg)
   %if p_lsap = 0 %start
      p_open = port open   ;! Reopenif 0
   %Else
      p_open = port closed
   %finish
%End

%predicate check transmit length (%Record(messagefm)%name msg,
                                  %integer min,max)
%Record(reqfm)%name r == record(addr(msg_data(1)))
   %trueif min <= r_length <= max
   r_result = bad transmit length
   Reply to client (msg)
   %false
%End

%Routine Transmit Block (%Record(messagefm)%name msg, %integer state)
%Record(reqfm)%name r == record(addr(msg_data(1)))
%Record(portfm)%name p
%switch sw(0:3)
   p == ports(r_port)
   -> sw(state)

sw(0):   ;! User request in
   %returnunless check port number (msg)
   %Returnunless check port status (msg,port open)
!   %if p_lsap = 0 %start
!      %returnunless check transmit length (r,min0,max0)
!   %Else
!      %Returnunless check transmit length (r,min,max)
!   %Finish
      
   %if p_tcurr ## NIL %start
      enqueue (msg,p_tpend)
      %Return
   %Finish
   p_tcurr == msg
   p_outbuff = addr(r_b(0))
   p_lout = r_length
{  oldack = oldack&\(1<<p_lsap)
{  oldnak = oldnak&\(1<<p_lsap)
   wec(DTX); wed (p_lsap)
   p_tmode = await RDY
sw(await RDY):
   %Return

sw(await ACK): ;! Get here if ACK arrives
   r_result = success
   -> next

sw(await NAK): ;! Come here if NAK arrived
   r_result = packet was naked
   -> next

next:
   Reply to Client (msg)
   p_tcurr == NIL; p_tmode = 0
   msg == dequeue(p_tpend)
   transmit block (msg,0) %unless msg == NIL
%End

%Routine Receive Block (%Record(messagefm)%Name msg, %integer state)
%Record(reqfm)%name r == record(addr(msg_data(1)))
%Record(portfm)%name p
%switch sw(0:2)
   p == ports(r_port)
   -> sw(state)

sw(0): ;! User request in
   %returnunless check port number (msg)
   %Returnunless check port status (msg,port open)
   
   %if p_rcurr ## NIL %Start
      enqueue (msg,p_rpend)
      %Return
   %Finish
   p_rcurr == msg
   p_inbuff = addr(r_b(0))
   p_lin = r_maxbytes
   %if p_rmode # pending DTX %start
      p_rmode = await DTX
      %Return
   %Finish
{  olddtx = olddtx&\(1<<p_lsap)

sw(await DTX):   ;! DTX has arrived
   p_rmode = await ETX
   wec (rdy); wed(p_lsap)
   %Return

sw(await ETX):   ;! STX (really ETX) has arrived
   r_resbytes = p_resbytes
   p_rmode = 0
   p_rcurr == NIL
   r_result = success
   Reply to Client (msg)
   msg == dequeue (p_rpend)
   receive block (msg,0) %unless msg == NIL
%End

%Routine Station Address (%Record(messagefm)%name msg)
%Record(reqfm)%name r == record(addr(msg_data(1)))
   r_result = success
   r_port = ldte
   Reply to Client (msg)
%End

%routine phex1(%integer x)
  x = x&15; x = x+7 %if x>9; printsymbol(x+'0')
%end

%routine phex2(%integer x)
  phex1(x>>4;x)
%end

%routine phex4(%integer x)
  phex2(x>>8;x)
%end

%routine phex(%integer x)
  phex4(x>>16;x)
%end
      
%Routine Handle Ether Event (%Integer eport,etype)
%Switch sw(0:255)
%Record(portfm)%Name p
   
   %Routine Ether Fail (%String(255) s)
      printsymbol(7);printsymbol(' ')
      printstring(s);printsymbol(' ')
      phex2(eport);printsymbol(' ');phex2(etype)
      printsymbol(nl)
   %End

   p == ports(eport)
   -> sw(etype)

sw(rdy):
   %if p_tmode = await RDY %start
      %if p_open = port closing %start
         Close Port (p_mgmt, await RDY)
      %Else
         Ether Fail ("Unexpected RDY (new)")
         Transmit Block (p_tcurr,await RDY)
      %Finish
   %elseif p_open = port opening
      Open Port (p_mgmt,await RDY)
   %Else
      Ether Fail ("Unexpected RDY")
   %Finish
   %Return
      
sw(etx):
   %if p_open = port closing %start
      close port (p_mgmt,await ETX)
   %elseif p_rmode = await ETX
      Receive Block (p_rcurr,await ETX)
   %Else
      Ether Fail ("Unexpected ETX")
   %Finish
   %Return

sw(dtx):
   %if p_open = port closing %start
      wec (ack); wed(eport)
   %elseif p_rmode=0 %and p_rcurr==nil
      p_rmode = pending DTX
   %Else
      phex2(p_rmode;p_open)
      Ether Fail (" Unexpected DTX")
      Receive Block (p_rcurr,await DTX)
   %Finish
   %Return

sw(nak):
   %if p_tmode = await ACK %start
      %if p_open = port closed %start
         p_tmode = 0
      %Else
         Transmit Block (p_tcurr,await NAK)
      %Finish
   %Else
      Ether Fail ("Unexpected NAK")
   %Finish
   %Return

sw(ack):
   %if p_tmode = await ACK %start
      %if p_open = port closed %start
         p_tmode = 0
      %Else
         Transmit Block (p_tcurr,await ACK)
      %Finish
   %Else
      Ether Fail ("Unexpected ACK")
   %Finish
   %Return

sw(*): 
   Ether Fail ("Unexpected Ether Event")
%End


%Routine Handle Request (%Record(messagefm)%Name msg)
%Record(reqfm)%name r == record(addr(msg_data(1)))
%Switch sw(1:7)
   -> sw(r_code) %if 1 <= r_code <= 7
   r_result = bad function code
   reply to client (msg);       %Return
sw(1):  Claim Port (msg);       %Return
sw(2):  Free Port (msg);        %Return
sw(3):  Open Port (msg,0);      %Return
sw(4):  Close Port (msg,0);     %Return
sw(5):  Transmit Block (msg,0); %Return
sw(6):  Receive Block (msg,0);  %Return
sw(7):  Station Address (msg);  %Return
%End

%Routine Initialise
%Integer i
%record(portfm)%name cp
   %For i = 0, 1, Max Port %cycle
      cp == ports (i)
      cp_open = port closed; cp_lsap = i
      cp_owned = port free
      setup queue (cp_tpend)
      setup queue (cp_rpend)
      cp_tcurr == NIL; cp_tmode = 0
      cp_rcurr == NIL; cp_rmode = 0
      cp_mgmt == NIL
   %Repeat      
   ports(0)_open = port open   ;! Port 0 always open
   eevent == create semaphore (0)
   mbox == create mailbox (eevent)
   define kernel object(mbox,"Ether mailbox")
!! olderr = 0; olddtx = 0; oldack = -1; oldnak = 0
   Setup Interrupts
{  Print String ("Ether station = ")
   wec(16_0F)     ;! Reset Station
   wec(esa)       ;! Enquire LDTE
   %Cycle; %Repeatuntil ldte # 0
{  phex2 (ldte) ; printsymbol(nl)
%End

%Predicate Remove Control (%Integername port,type)
%Integer t
   t = out
   %falseif t=in
   port = byteinteger(t)
   type = byteinteger(t+maxctrl)
   t = t + 1
   t = bs %if t=be
   out = t
   %true
%End

%Begin
%integer port,etype
   become process(2000)
   Initialise
   %Cycle
      Semaphore Wait (eEvent)
      %if remove control (port, etype) %start
         Handle Ether Event (port, etype)
      %else
         signal semaphore (eEvent)
         handle request (receive message (mbox))
      %Finish
   %Repeat
%End
