! LANCE driver for interim Mouse, GDMR, Jan 1988.

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

%constinteger RX buffer size = 1600
%constinteger RX buffers     = 48

%constinteger max inbound slot = 8

%include "Moose:Mouse.Inc"
%include "GDMR_E:Lance.Inc"
%include "GDMR_E:Ether.Inc"
%include "GDMR_E:L_Cntrl.Inc"

%systemintegerfnspec free store
%systemintegerfnspec global heap get(%integer size)
%systemroutinespec phex(%integer i)
%systemroutinespec phex2(%integer i)
%systemroutinespec phex4(%integer i)

%externalroutinespec FS insert(%string(31) what, %integer label)
%externalpredicatespec FS lookup(%string(31) what, %integername value)

!! %include "GDMR_H:Dump.Inc"

%owninteger control = 0

%constinteger RX size    = 10240, RX priority    = 7
%constinteger TX size    = 10240, TX priority    = 7
%constinteger error size = 10240, error priority = 7

%constinteger lance IPL = 3
@16_00FE0000 %writeonly %volatile %half lance address, lance data

%constinteger C err  bit = 15
%constinteger C babl bit = 14
%constinteger C cerr bit = 13
%constinteger C miss bit = 12
%constinteger C merr bit = 11
%constinteger C rint bit = 10
%constinteger C tint bit =  9
%constinteger C idon bit =  8
%constinteger C intr bit =  7
%constinteger C inea bit =  6
%constinteger C rxon bit =  5
%constinteger C txon bit =  4
%constinteger C tdmd bit =  3
%constinteger C stop bit =  2
%constinteger C strt bit =  1
%constinteger C init bit =  0

%constinteger clear interrupt = C babl ! C miss ! C merr ! %c
                                C rint ! C tint ! C idon

%recordformat ether address fm(%bytearray x(0 : 5))
%recordformat ether header fm(%record(ether address fm) destination,
                              %record(ether address fm) source,
                              %short type)

%ownrecord(ether address fm) our ether address = 0

%ownrecord(semaphore fm) RX semaphore = 0
%ownrecord(semaphore fm) TX semaphore = 0
%ownrecord(semaphore fm) error semaphore = 0

%owninteger babl tally = 0
%owninteger miss tally = 0
%owninteger merr tally = 0
%owninteger fram tally = 0
%owninteger oflo tally = 0
%owninteger CRC  tally = 0
%owninteger buff tally = 0
%owninteger more tally = 0
%owninteger one  tally = 0
%owninteger def  tally = 0
%owninteger uflo tally = 0
%owninteger lcol tally = 0
%owninteger lcar tally = 0
%owninteger rtry tally = 0

%owninteger no takers tally = 0
%owninteger packets in = 0
%owninteger bytes in = 0
%owninteger packets out = 0
%owninteger bytes out = 0

%ownrecord(ibf) ib = 0
%ownrecord(drf)%name RX ring == nil
%ownrecord(drf)%name TX ring == nil

! Initialisation and interrrupt handler

%routine initialise lance
   %ownrecord(interrupt handler fm) handler = 0
   %ownrecord(semaphore fm) initialisation semaphore = 0
   %label H, TX, babl, miss, merr, idon, done
      ! Turn off the chip before we do anything else
      ! (just to be safe...)
      lance address = 0;  lance data = c stop
      ! Now set up data structures
      setup semaphore(initialisation semaphore)
      setup interrupt handler(handler, addr(H))
      add interrupt handler(handler, lance IPL)
      ! Initialise the descriptor rings and the board here, then
      ! start it all going.  Note that we ASSUME that once the chip is
      ! initialised we will only be accessing lance register CSR0, and
      ! hence we won't need to keep selecting it.....
      RX ring == record((global heap get(size of(RX ring) + 8) + 7) & (\ 7))
      TX ring == record((global heap get(size of(TX ring) + 8) + 7) & (\ 7))
      RX ring = 0;  TX ring = 0
      FS insert("LANCE_TX_RING", addr(TX ring))
      FS insert("LANCE_RX_RING", addr(RX ring))
      !! printstring("RX ring at ");  phex(addr(RX ring))
      !! printstring(", TX ring at ");  phex(addr(TX ring));  newline
      ib = 0;  ! Clear extraneous stuff
      ib_mode = 0;  ! Enable TX, RX, CRC 
      ib_pa0 = our ether address_x(1) << 8 ! our ether address_x(0)
      ib_pa2 = our ether address_x(3) << 8 ! our ether address_x(2)
      ib_pa4 = our ether address_x(5) << 8 ! our ether address_x(4)
      ib_lowrad <- addr(RX ring) & 16_FFFF
      ib_highrad <- (addr(RX ring) >> 16) & 255
      ib_rcount = 16_E0;  ! 128 buffers
      ib_lowtad <- addr(TX ring) & 16_FFFF
      ib_hightad <- (addr(TX ring) >> 16) & 255
      ib_tcount = 16_E0;  ! 128 buffers
      lance address = 1;  lance data <- addr(ib) & 16_FFFF
      lance address = 2;  lance data <- (addr(ib) >> 16) & 255
      lance address = 3;  lance data = bswp
      lance address = 0;  lance data = c init ! c strt ! c inea
      semaphore wait(initialisation semaphore)
      !! printstring("LANCE initialised, CSR0 ");  phex4(lance data);  newline
      %return

H:    ! Interrupt handler.  CSR0 assumed selected.  We read the CSR
      ! once only -- perhaps we should reread it for each section?
      *move.w lance data, D1                 { Get CSR0
      *btst.w #C intr bit, D1                { Did we interrupt?
      *beq done                              { No, ->

      !! put long(D1);  put sym(13);  put sym(10)

      *btst.w #C rint bit, D1                { RX interrupt?
      *beq TX                                { No, ->
      int signal semaphore(RX semaphore)     { Kick RX process
      lance data = C rint ! C inea           { Clear RX interrupt
      -> H                                   { Round for another one

TX:   *btst.w #C tint bit, D1                { TX interrupt?
      *beq babl                              { No, ->
      int signal semaphore(TX semaphore)     { Kick TX process
      lance data = C tint ! C inea           { Clear TX interrupt
      -> H                                   { Round for another one

babl: *btst.w #C babl bit, D1                { Babbling?
      *beq miss                              { No, ->
      *addq.l #1, babl tally                 { Note error
    ! int signal semaphore(error semaphore)  { Kick error process
      lance data = C babl ! C inea           { Clear babble bit
      -> H                                   { Round for another one

miss: *btst.w #C miss bit, D1                { Missed packet?
      *beq merr                              { No, ->
      *addq.l #1, miss tally                 { Note error
    ! int signal semaphore(error semaphore)  { Kick error process
      lance data = C miss ! C inea           { Clear miss bit
      -> H                                   { Round for another one

merr: *btst.w #C merr bit, D1                { Memory error?
      *beq idon                              { No, ->
      *addq.l #1, merr tally                 { Note error
    ! int signal semaphore(error semaphore)  { Kick error process
      lance data = C merr ! C inea           { Clear memory error (but the chip
                                             { has stopped...)
      -> H                                   { Round for another one

idon: *btst.w #C idon bit, D1                { Initialisation done?
      *beq done                              { No, no more to check, ->
      int signal semaphore(initialisation semaphore)
      lance data = C idon ! C inea           { Clear init done
      -> H                                   { Round for another one

done: return from interrupt
      *move.l D0, D0
%end


! Receiver buffer management

%recordformat RX buffer fm(%bytearray data(1 : RX buffer size),
                           %integer bytes,
                           %record(RX buffer fm)%name next)
%constinteger RX total size = RX buffer size + 8
! Extra fields at tail rather than head so that buffer address can be
! extracted directly from the descriptor ring entry.

%ownrecord(semaphore fm) RX add semaphore = 0

%routine add RX to ring(%record(RX buffer fm)%name b)
   %owninteger next add position = 0
   %record(df)%name entry
      !! printstring("Add RX buffer at ");  phex(addr(b))
      !! printstring(" to slot ");  write(next add position, 0);  newline
      semaphore wait(RX add semaphore)
      entry == RX ring_desc(next add position)
      %if entry_control & R own # 0 %start
         ! Owned by the chip.  Disaster
         printstring("Lance RX slot ");  write(next add position, 0)
         printstring(" owned by chip");  newline
         %stop
      %finish
      entry_lowad <- addr(b) & 16_FFFF
      entry_highad <- (addr(b) >> 16) & 255
      entry_minus = -RX buffer size
      entry_plus = 0
      entry_control = R own;  ! Give it to the chip
      next add position = (next add position + 1) & 127
      signal semaphore(RX add semaphore)
%end

%recordformat inbound slot fm(%integer type,
                              %record(RX buffer fm)%name buffers,
                              %record(ether request fm)%name requests)

%ownrecord(inbound slot fm)%array inbound slots(1 : max inbound slot) = 0(*)
%ownrecord(semaphore fm) inbound semaphore = 0

%routine show ether address(%record(ether address fm)%name a)
   %integer i
      phex2(a_x(i)) %and space %for i = 0, 1, 5
%end

%routine copy inbound(%record(RX buffer fm)%name b,
                      %record(ether request fm)%name r)
   %integer from, to, bytes
   %label CL
      from = addr(b_data(1))
      to = addr(r_buffer)
      bytes = b_bytes - 1
      !! printstring("Copy inbound: ");  write(bytes + 1, 0)
      !! printstring(" from ");  phex(from)
      !! printstring(" to ");  phex(to);  newline
      *move.l from, A0
      *move.l to, A1
      *move.L bytes, D0
   CL:*move.b (A0)+, (A1)+
      *dbra D0, CL
      r_bytes = b_bytes
%end

%routine process inbound(%record(RX buffer fm)%name b)
   %record(RX buffer fm)%name lb
   %record(ether header fm)%name h
   %record(inbound slot fm)%name in, all == nil
   %record(ether request fm)%name r
   %integer i
      packets in = packets in + 1
      bytes in = bytes in + b_bytes
      h == record(addr(b_data(1)))
      !! printstring("To:   ");  show ether address(h_destination);  newline
      !! printstring("From: ");  show ether address(h_source);  newline
      !! printstring("Type: ");  phex4(h_type);  newline
      !! dump(b_bytes - 14, b_data(15))
      semaphore wait(inbound semaphore)
      %for i = 1, 1, max inbound slot %cycle
         in == inbound slots(i)
         %if in_type = h_type %or (0 < h_type <= 1500 %and in_type = 1) %start
            ! Match.
            !! printstring("Match");  newline
matching:   %if in_requests ## nil %start
               ! Read request pending.  Unlink it, copy in our data
               ! and send it back to the requestor.
               !! printstring("Pending request");  newline
               r == in_requests
               in_requests == r_next
               signal semaphore(inbound semaphore)
               copy inbound(b, r)
               r_status = ether success
               send message(r, r_system part_reply, nil) %c
                  %if r_system part_reply ## nil
               ! Now return the buffer to the pool
               add RX to ring(b)
            %else
               ! No request pending.  Enqueue our buffer on the end of
               ! the pending buffer list.
               !! printstring("Link us on");  newline
               b_next == nil
               %if in_buffers == nil %start
                  in_buffers == b
               %else
                  lb == in_buffers
                  lb == lb_next %while lb_next ## nil
                  lb_next == b
               %finish
               signal semaphore(inbound semaphore)
            %finish
            %return
         %else %if in_type < 0
            ! Catch-all type
            all == in
         %finish
      %repeat
      in == all %and -> matching %if all ## nil;  ! Catch-all
      signal semaphore(inbound semaphore)
      %if control & trace no takers # 0 %start
         show ether address(h_source);  printstring(" -> ")
         show ether address(h_destination);  printstring(" type ")
         phex4(h_type);  printstring(" dropped");  newline
      %finish
      no takers tally = no takers tally + 1
      add RX to ring(b);  ! Free it up again
%end


! Transmit data structures.  Parallel to the TX ring we keep a ring of
! request pointers: when we add a request to the TX ring we note its
! corresponding message in the request ring for later use by the interrupt
! handler.

%ownrecord(ether request fm)%namearray TX requests(0 : 127) == nil(*)

%routine add TX request(%record(ether request fm)%name r)
   %owninteger next add position = 0
   %record(df)%name entry
      !! printstring("Add TX request: ");  write(r_bytes, 0);  newline
      !! dump(r_buffer, r_bytes)
      entry == TX ring_desc(next add position)
      %if entry_control & T own # 0 %start
         ! Slot owned by device.  All we can do is bounce the
         ! user's request.
         !! printstring("Not owned, bounce it");  newline
         r_status = ether no free slot
         send message(r, r_system part_reply, nil) %c
            %if r_system part_reply ## nil
         %return
      %finish
      ! Slot is OK.  Note the request, then set up the TX entry.
      packets out = packets out + 1
      bytes out = bytes out + r_bytes
      TX requests(next add position) == r
      entry_lowad <- addr(r_buffer) & 16_FFFF
      entry_highad <- (addr(r_buffer) >> 16) & 255
      entry_minus = -r_bytes
      entry_plus = 0
      ! Now kick the chip
      !! printstring("About to kick the chip");  newline
      entry_control = T own ! T stp ! T enp
{TD}  lance data = C tdmd ! C inea
      ! On to next slot
      next add position = (next add position + 1) & 127
%end


! Interrupt handling processes

%routine RX process
   %owninteger next remove position = 0
   %record(RX buffer fm)%name b
   %record(df)%name entry
   %integer x   
      open input(2, ":N");  select input(2)
      open output(2, ":T");  select output(2)
      %cycle
         semaphore wait(RX semaphore)
         !! printstring("RX semaphore signalled");  newline
         %cycle
            !! printstring("Remove RX entry at ")
            !! write(next remove position, 0);  newline
            entry == RX ring_desc(next remove position)
            %if entry_control & R own = 0 %and entry_minus # 0 %start
               ! We own it and haven't already processed it.
               ! Get the address, check the status
               x = entry_highad << 16 ! entry_lowad
               !! printstring("Buffer at ");  phex(x);  newline
               b == record(x)
               !! printstring("Flags ");  phex2(entry_control);  newline
               %if entry_control & R err = 0 %start
                  ! Packet is OK.  Note its size and process it.
                  ! The book says the size is in BCD (p.26), but this
                  ! appears to be fictitious....
                  b_bytes = entry_plus
                  process inbound(b)
               %else
                  ! Error in this one.  Tally it, then put the
                  ! buffer back on the receive ring.
                  fram tally = fram tally + 1 %if entry_control & R fram # 0
                  oflo tally = oflo tally + 1 %if entry_control & R oflo # 0
                  CRC  tally = CRC  tally + 1 %if entry_control & R CRC  # 0
                  buff tally = buff tally + 1 %if entry_control & R buff # 0
                  !! printstring("Lance: dropping ");  phex2(entry_control)
                  !! printstring(" FRAM") %if entry_control & R fram # 0
                  !! printstring(" OFLO") %if entry_control & R oflo # 0
                  !! printstring(" CRC" ) %if entry_control & R CRC  # 0
                  !! printstring(" BUFF") %if entry_control & R buff # 0
                  !! newline
                  add RX to ring(b)
                  signal semaphore(error semaphore)
               %finish
               entry_minus = 0;  ! So we know it's been seen....
               next remove position = (next remove position + 1) & 127
            %else
               ! Owned by chip or already processed, back to sleep
               %exit
            %finish
         %repeat
      %repeat
%end

%routine TX process
   %owninteger next remove position = 0
   %record(ether request fm)%name r
   %record(df)%name entry
      open input(2, ":N");  select input(2)
      open output(2, ":T");  select output(2)
      %cycle
         semaphore wait(TX semaphore)
         !! printstring("TX semaphore signalled");  newline
         %cycle
            entry == TX ring_desc(next remove position)
            !! phex2(entry_control);  space;  phex4(entry_plus);  space
            !! phex2(entry_highad);  phex4(entry_lowad);  space
            !! write(entry_minus, 0);  newline
            %if entry_control & T own = 0 %and entry_control & T stp # 0 %start
               ! We own it, start of packet.  Check the status, tell the user.
               ! Beware spurious signals -- we assume that if stp isn't
               ! set then the apparently-completing request is bogus.
               r == TX requests(next remove position)
               TX requests(next remove position) == nil
               %if entry_control & T err = 0 %start
                  r_status = ether success %if r ## nil
               %else
                  r_status = ether TX failed %if r ## nil
                  buff tally = buff tally + 1 %if entry_plus & D buff # 0
                  uflo tally = uflo tally + 1 %if entry_plus & D uflo # 0
                  lcol tally = lcol tally + 1 %if entry_plus & D lcol # 0
                  lcar tally = lcar tally + 1 %if entry_plus & D lcar # 0
                  rtry tally = rtry tally + 1 %if entry_plus & D rtry # 0
                  !! printstring("Lance: TX ");  phex2(entry_plus)
                  !! printstring(" BUFF") %if entry_plus & D buff # 0
                  !! printstring(" UFLO") %if entry_plus & D uflo # 0
                  !! printstring(" LCOL") %if entry_plus & D lcol # 0
                  !! printstring(" LCAR") %if entry_plus & D lcar # 0
                  !! printstring(" RTRY") %if entry_plus & D rtry # 0
                  !! newline
               %finish
               send message(r, r_system part_reply, nil) %c
                  %if r ## nil %and r_system part_reply ## nil
               more tally = more tally + 1 %if entry_control & T more # 0
               one  tally = one  tally + 1 %if entry_control & T one  # 0
               def  tally = def  tally + 1 %if entry_control & T def  # 0
               entry_control = 0;  ! Mark it requestless
               next remove position = (next remove position + 1) & 127
            %else
               ! Owned by the chip or bogus, back to sleep
               %exit
            %finish
         %repeat
      %repeat
%end

!%routine error process
!   %owninteger last babl tally = 0
!   %owninteger last miss tally = 0
!   %owninteger last merr tally = 0
!   %owninteger last fram tally = 0
!   %owninteger last oflo tally = 0
!   %owninteger last CRC  tally = 0
!   %owninteger last buff tally = 0
!      open input(2, ":N");  select input(2)
!      open output(2, ":T");  select output(2)
!      %cycle
!         semaphore wait(error semaphore)
!         printstring("Lance: BABL ") %if babl tally # last babl tally
!         printstring("Lance: miss ") %if miss tally # last miss tally
!         printstring("Lance: MErr ") %if merr tally # last merr tally
!         %if babl tally # last babl tally %or miss tally # last miss tally %c
!         %or merr tally # last merr tally %or fram tally # last fram tally %c
!         %or oflo tally # last oflo tally %or CRC  tally # last CRC  tally %c
!         %or buff tally # last buff tally %start
!            printstring("Lance: ")
!            write(packets in, 0);  printstring(" in, ")
!            write(babl tally, 0);  printstring(" babl, ")
!            write(miss tally, 0);  printstring(" miss, ")
!            write(merr tally, 0);  printstring(" merr, ")
!            write(fram tally, 0);  printstring(" fram, ")
!            write(oflo tally, 0);  printstring(" oflo, ")
!            write(CRC  tally, 0);  printstring(" CRC, ")
!            write(buff tally, 0);  printstring(" buff")
!            newline
!            last babl tally = babl tally;  last miss tally = miss tally
!            last merr tally = merr tally;  last fram tally = fram tally
!            last oflo tally = oflo tally;  last CRC  tally = CRC  tally
!            last buff tally = buff tally
!         %finish
!      %repeat
!%end


! User request interpreter

%routine interpret request(%record(ether request fm)%name r)
   %record(inbound slot fm)%name in
   %record(RX buffer fm)%name b
   %record(ether request fm)%name lr
   %record(ether header fm)%name h
   %record(ether address fm)%name a
   %record(ether statistics fm)%name stats
   %integer i
      %if r_code = ether read %start
         !! printstring("Ether read");  newline
         %unless 0 < r_slot <= max inbound slot %start
            r_status = ether undefined
            -> send
         %finish
         in == inbound slots(r_slot)
         %if in_type = 0 %start
            r_status = ether undefined
            -> send
         %finish
         semaphore wait(inbound semaphore)
         %if in_buffers == nil %start
            ! Nothing pending, we'll have to queue.
            !! printstring("Queueing");  newline
            r_next == nil
            %if in_requests == nil %start
               in_requests == r
            %else
               lr == in_requests
               lr == lr_next %while lr_next ## nil
               lr_next == r
            %finish
            signal semaphore(inbound semaphore)
            %return
         %else
            ! There's a buffer pending.  Dequeue it and copy its data
            ! into our buffer.
            !! printstring("Buffer pending");  newline
            b == in_buffers
            in_buffers == b_next
            signal semaphore(inbound semaphore)
            copy inbound(b, r)
            add RX to ring(b);  ! Give buffer back to chip
            r_code = ether success
            -> send
         %finish
      %else %if r_code = ether write
         !! printstring("Ether write ");  newline
         %unless 0 < r_slot <= max inbound slot %and 60 <= r_bytes <= 1500 %start
            r_status = ether undefined
            -> send
         %finish
         in == inbound slots(r_slot)
         %if in_type <= 0 %start;  ! NB: don't allow catch-all to send
            r_status = ether undefined
            -> send
         %finish
         h == record(addr(r_buffer))
         h_source = our ether address
         h_type = in_type %if in_type > 1500;  ! Not ISO
         add TX request(r)
         %return
      %else %if r_code = ether register %and %c
                (r_type = 1 {ISO} %or r_type = -1 {all} %or 1500 < r_type <= 16_7FFF)
         !! printstring("Register ");  phex4(r_type);  newline
         semaphore wait(inbound semaphore)
         %for i = 1, 1, max inbound slot %cycle
            in == inbound slots(i)
            %if in_type = 0 %start
               ! This one's free
               !! printstring("Slot ");  write(i, 0);  newline
               in_type = r_type
               signal semaphore(inbound semaphore)
               r_slot = i
               r_status = ether success
               -> send
            %finish
         %repeat
         signal semaphore(inbound semaphore)
         r_status = ether no free slot
      %else %if r_code = ether unregister
         r_status = ether undefined
      %else %if r_code = ether address
         a == record(addr(r_buffer))
         a = our ether address
         r_bytes = 6
         r_status = ether success
      %else %if r_code = ether statistics
         stats == record(addr(r_buffer))
         stats_address(i) = our ether address_x(i) %for i = 0, 1, 5
         stats_packets in = packets in
         stats_bytes in = bytes in
         stats_packets out = packets out
         stats_bytes out = bytes out
         stats_no takers tally = no takers tally
         stats_babl tally = babl tally
         stats_miss tally = miss tally
         stats_merr tally = merr tally
         stats_fram tally = fram tally
         stats_oflo tally = oflo tally
         stats_CRC  tally = CRC  tally
         stats_buff tally = buff tally
         stats_more tally = more tally
         stats_one  tally = one  tally
         stats_def  tally = def  tally
         stats_uflo tally = uflo tally
         stats_lcol tally = lcol tally
         stats_lcar tally = lcar tally
         stats_rtry tally = rtry tally
         r_bytes = 4 * 19
         r_status = ether success
      %else %if r_code = ether show CSR0
         ! Print out the contents of the Lance's CSR0 register
         printstring("LANCE: CSR0 = ");  phex4(lance data)
         newline
         r_status = ether success
      %else
         r_status = ether undefined
      %finish
send: send message(r, r_system part_reply, nil) %if r_system part_reply ## nil
%end


! Main program.  Start it going, then wait for user requests to interpret.

%ownrecord(semaphore fm) request semaphore = 0
%ownrecord(mailbox fm) request mailbox = 0

%begin
   %record(ether request fm)%name r
   %record(process fm)%name p
   %integer i
   %label start RX, start TX ;!, start error
      open input(3, ":N");  select input(3)
      open output(3, ":T");  select output(3)
      %unless FS lookup("SLOW_ETHER_ADDRESS", i) %start
         printstring("Lance: Address unknown");  newline
         %stop
      %finish
      our ether address_x(0) = 'F';  our ether address_x(1) = 'R'
      our ether address_x(2) = 'E';  our ether address_x(3) = 'D'
      our ether address_x(4) = '-';  our ether address_x(5) = i & 255
      setup semaphore(RX add semaphore)
      signal semaphore(RX add semaphore)
      setup semaphore(RX semaphore)
      setup semaphore(TX semaphore)
      setup semaphore(error semaphore)
      setup semaphore(inbound semaphore)
      signal semaphore(inbound semaphore)
      setup semaphore(request semaphore)
      setup mailbox(request mailbox, request semaphore)
      FS insert(ether mailbox name, addr(request mailbox))
      FS insert("LANCE_CONTROL", addr(control))
      p == create process(RX size, addr(start RX), RX priority, nil)
      p == create process(TX size, addr(start TX), TX priority, nil)
      !p == create process(error size, addr(start error), error priority, nil)
      initialise lance
      add RX to ring(record(global heap get(RX total size))) %c
         %for i = 1, 1, RX buffers
      {} printstring("LANCE: ");  write(free store, 0)
      {} printstring(" free, address ")
      {} %for i = 0, 1, 5 %cycle
      {}    print symbol('-') %if i # 0
      {}    phex2(our ether address_x(i))
      {} %repeat
      {} newline

      %cycle
         r == receive message(request mailbox)
         interpret request(r)
      %repeat

start RX:      RX process
start TX:      TX process
!start error:   error process

%end %of %program
