{Test program for Intel-82586-based APM local bus 10MHz Ethernet board}

%externalintegerfnspec testsymbol
%begin
!option "-low-nocheck-nodiag-nons-nowarn"
%option "-low-nons"

! ** IMPORTANT ** BYTE SEX **

! The interface is wired for BYTE-FIDELITY, i.e. values which appear in
! consecutive byte locations as viewed by the 586 do so as viewed by the
! CPU as well.  Since 82586 and 68010 have different byte sex this implies
! that values which are treated as 16-bit words have to have their bytes
! transposed in software, likewise for 32-bit words (which, incidentally,
! are invariably 24-bit addresses, in which only 17 bits are relevant).

! Memory layout

! The board contains 128 kbytes of dual ported memory, which appears at
! 040000:05FFFF in the CPU address space.  Address consistency between 586
! and CPU is achieved by ignoring the 586's seven high-order address bits.
! For example, the "System Configuration Pointer" record, which the 586
! assumes is at FFFFF6, is actually at 05FFF6.  The CA (Channel Attention)
! signal to the 586 is generated as a side effect of the CPU reading from
! any of the 16 highest-addressed bytes of the shared memory.
! NOTE:  BASE is the middle of the memory, because of the 64k segmentation.
! All descriptors are kept in the upper half, data buffers are in the lower
! half and spill over into the upper.

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

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

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

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

%routine phex(%integer x)
  phex4(x>>16); phex4(x)
%end

%ownbytearray my address (1:6) = 'F','R','E','D','-',16_14
%ownbytearray yr address (1:6) = 'F','R','E','D','-',16_14

! Configuration constants

%constinteger buffersize=1514, rxbuffers=64, txbuffers=20

! Descriptors

%constinteger none=16_ffff

%recordformat scp fm  {System Configuration Pointer} -
(%byte sysbus,        {Data bus width: 1 for 8-bit bus, 0 for 16-bit bus}
       ca,            {Reading this generates Channel Attention}
 %integer *, iscp ad  {address of ISCP (see below)} )

%recordformat iscp fm {Intermediate System Control Pointer} -
(%byte busy, *,       {cleared by 586 after reading SCB ad}
 %half scb offset,    {Address of
 %integer scb base    {SCB (see below)} )

%recordformat scb fm  {System Control Block} -
(%half status, command,
  cbl offset, rfa offset,
  crc errs, aln errs,
  rsc errs, ovr errs)

! Buffers

%recordformat rxbuffer fm (%bytearray b(0:buffersize-1))
%recordformat txbuffer fm (%bytearray b(0:buffersize-1))

! Buffer descriptors
! (the NEXT INDEX fields are for internal consistency checking purposes)

%recordformat rxbd fm (%half count, next offset, %integer buffer ad,
                       %half size, next index)
%recordformat txbd fm (%half count, next offset, %integer buffer ad,
                       %half next index)

! Receive Frame and Command descriptors
! (The LINK INDEX fields are for checking.
!  The MCA setup command can take no more than 2 MC addresses
!  without increasing the size of the descriptor;  thus either
!  all descriptors would need to be bigger (wasting space) or
!  a separate special descriptor would need to be reserved for
!  this function;  depends on how many addresses are needed
!  could be up to 64 if setting all filter bits.)

%recordformat rfd fm -
(%half status, command, link offset, desc offset,
 %bytearray dest, source(1:6), %half type, link index)

%recordformat cmd fm -
(%half status, command, link offset,
 ({Transmit}  %half desc offset, %bytearray dest(1:6), %half type %or-
  {IA Setup}  %bytearray ia(1:6) %or-
  {Configure} %byte config count, %bytearray config data(1:11) %or-
  {MCA Setup} %half mca byte count, %bytearray mca(1:12) %or-
  {TDR}       %half time %or-
  {Dump}      %half buffer offset),
 %half link index)

%bytearrayname dumpbuf(0:169)
%record(cmd fm)%name com

@16_1070 {i.e. level 4} %integer interrupt handler

%constinteger base=16_50000 {in middle of memory space}

@(base-64*1024) %integerarray mem(0:32767)
@(base-64*1024) -
%record (rxbuffer fm) %array rx buffer (0:rxbuffers-1),
%record (txbuffer fm) %array tx buffer (0:txbuffers-1),
%record (rxbd fm) %array rxbd (0:rxbuffers-1),
%record (txbd fm) %array txbd (0:txbuffers-1),
%record (rfd fm) %array rfd (0:rxbuffers-1),
%record (cmd fm) %array cmd (0:txbuffers-1),
%integer old interrupt handler,nextr,nextc,
%integer intcount,cxcount,frcount,cnrcount,rnrcount,
%half intstatus, dummy {dummy MUST come last}

@(base+16_FFF6) %record (scp fm) scp
@(base+16_FFEE) %record (iscp fm) iscp
@(base+16_FFDE) %record (scb fm) scb

{Status/Command constants for SCB ** pre-swapped **}
%constinteger -
  status mask      = 16_00F0,
  cx               = 16_0080,
  fr               = 16_0040,
  cnr              = 16_0020,
  rnr              = 16_0010,
  cus mask         = 16_0007,
  cus idle         = 16_0000,
  cus suspended    = 16_0001,
  cus ready        = 16_0002,
  rus mask         = 16_7000,
  rus idle         = 16_0000,
  rus suspended    = 16_1000,
  rus no resources = 16_2000,
  rus ready        = 16_4000,
  cuc start        = 16_0001,
  cuc resume       = 16_0002,
  cuc suspend      = 16_0003,
  cuc abort        = 16_0004,
  ruc start        = 16_1000,
  ruc resume       = 16_2000,
  ruc suspend      = 16_3000,
  ruc abort        = 16_4000,
  reset            = 16_8000

{Command/descriptor status bits ** pre-swapped **}
%constinteger -
  done      = 16_80,
  busy      = 16_40,
  ok        = 16_20,
  aborted   = 16_10,
  el        = 16_80,
  eof       = 16_80,
  f bit     = 16_40,
  s bit     = 16_40,
  i bit     = 16_20

{Command codes ** pre-swapped **}
%constinteger -
  command nop       = 16_0000,
  command ia setup  = 16_0100,
  command configure = 16_0200,
  command multicast = 16_0300,
  command transmit  = 16_0400,
  command tdr       = 16_0500,
  command dump      = 16_0600,
  command diagnose  = 16_0700

%integerfn swap2(%integer a)
! Byte-sex-change a 2-byte word:   AB <-> BA
  *rol.w #8,d0; *and.l #16_ffff,d0
%end

%integerfn swap4(%integer a)
! Byte-sex-change a 4-byte word:  ABCD <-> DCBA
  *rol.w #8,d0; *swap d0; *rol.w #8,d0
%end

%integerfn offset(%name x)
  %result = swap2(addr(x)-base)
%end

%record(*)%map item at offset(%integer x)
  %result == nil %if x=none
  %result == record(swap2(x)+base)
%end

%routine channel attention
  *move.b scp_ca,d0
%end

%routine ord(%integer i)
  write(i,0); i = 0 %if 11<=rem(i,100)<=13; i = rem(i,10)
  %if i=1 %then printstring("st") %else-
  %if i=2 %then printstring("nd") %else-
  %if i=3 %then printstring("rd") %else printstring("th")
%end

%routine receive frame
%record(rfd fm)%name fdesc
%record(rxbd fm)%name bdesc
%record(rxbuffer fm)%name buffer==nil
%integer i
%owninteger active=0,count=1
  %if active=0 %start
    active = 1; scb_command = scb_command ! ruc start; channel attention
    %return
  %finish
  fdesc == rfd(nextr)
another:
  i = fdesc_status
  ord(count); printstring(" received frame ")
phex4(offset(fdesc)); space; phex4(fdesc_linkoffset); space
phex4(fdesc_descoffset); space
  bdesc == item at offset(fdesc_desc offset)
  buffer == record(swap4(bdesc_buffer ad)) %unless bdesc==nil
phex4(bdesc_nextoffset) %and space %unless bdesc==nil
  printstring("s="); phex4(swap2(i)); printsymbol(':')
  printstring(" DUN") %if i&done#0
  printstring(" BSY") %if i&busy#0
  printstring(" OK") %if i&ok#0
  printstring(" ABO") %if i&aborted#0
  printstring(" CRC") %if i&8#0
  printstring(" MAL") %if i&4#0
  printstring(" NRE") %if i&2#0
  printstring(" OVR") %if i&1#0
  printstring(" RNT") %if i&16_8000#0
  printstring(" NFL") %if i&16_4000#0
  newline
  phex2(fdesc_dest(i)) %for i = 1,1,6; space
  phex2(fdesc_source(i)) %for i = 1,1,6; space
  phex4(fdesc_type)
  %if bdesc==nil %then printstring(" No data buffer used") %elsestart
x:  space; phex4(swap2(bdesc_count))
    space %and phex2(buffer_b(i)) %for i = 0,1,14
    %if bdesc_count&el=0 %start
      bdesc == item at offset(bdesc_next offset)
      buffer == record(swap4(bdesc_buffer ad))
      newline; spaces(30); ->x
    %finish
  %finish
  newline
  %returnunless fdesc_status&done#0
  count = count+1
  fdesc_status = 0
  nextr = rxbuffers %if nextr=0; nextr = nextr-1
  fdesc == rfd(nextr); ->another %if fdesc_status&done#0
%end

%routine check interrupts
%owninteger crc=0,aln=0,rsc=0,ovr=0,errors=0
%owninteger ic=0,status=0
  %returnif ic=intcount %and status=intstatus
  ic = intcount; status = intstatus
  ord(ic); printstring(" interrupt "); phex4(swap2(status))
  printstring(" CX") %if status&cx#0
  printstring(" FR") %if status&fr#0
  printstring(" CNR") %if status&cnr#0
  printstring(" RNR") %if status&rnr#0
  printstring(", CU "); d0 = status&cusmask
  %if d0=cusidle %then printstring("IDL") %else-
  %if d0=cussuspended %then printstring("SUS") %else-
  %if d0=cusready %then printstring("BSY") %else-
    printstring("???")
  printstring(", RU "); d0 = status&rusmask
  %if d0=rusidle %then printstring("IDL") %else-
  %if d0=russuspended %then printstring("SUS") %else-
  %if d0=rusnoresources %then printstring("NRE") %else-
  %if d0=rusready %then printstring("RDY") %else-
    printstring("???")
  crc = swap2(scb_crcerrs)
  aln = swap2(scb_alnerrs)
  rsc = swap2(scb_rscerrs)
  ovr = swap2(scb_ovrerrs)
  %unless crc+aln+rsc+ovr=errors %start
    errors = crc+aln+rsc+ovr
    printstring(", CRC ALN RSC OVR: ")
    phex4(crc); space; phex4(aln); space; phex4(rsc); space; phex4(ovr)
  %finish
  newline
  receive frame %if rfd(nextr)_status&done#0
%end

%owninteger seq=1480

%routine transmit frame
%record(txbd fm)%name bdesc
%record(txbuffer fm)%name buffer
%owninteger count=1
%integer prev,status,i
  seq = seq+1
  com == cmd(nextc)
  com_status = 0; com_command = command transmit ! sbit
  com_dest = yr address
  com_type = 16_1234+seq
  bdesc == txbd(nextc)
  com_desc offset = offset(bdesc)
  buffer == txbuffer(nextc)
  bdesc_count = eof+swap2(|2-seq|)
  buffer_b(i) = ((i+1)*17+seq)&255 %for i = 0,1,63
  scb_command = cuc resume
  channel attention
  %cycle
    %cycle
      status = com_status; i = testsymbol
      %exitif status&done#0 %or i>=0
    %repeat
    ord(count); printstring(" transmission ")
    phex4(swap2(status)); printsymbol(':')
    printstring(" DONE") %if status&done#0
    printstring(" BSY") %if status&busy#0
    printstring(" OK") %if status&ok#0
    printstring(" ABO") %if status&aborted#0
    printstring(" NCR,") %if status&16_4#0
    printstring(" NCT,") %if status&16_2#0
    printstring(" URN,") %if status&16_1#0
    printstring(" DEF,") %if status&16_8000#0
    printstring(" HBT,") %if status&16_4000#0
    printstring(" TMC,") %if status&16_2000#0
    printstring(" COL="); write(status>>8&15,0); newline
  %repeatuntil status&done#0 %or i='.'
  %returnif status&done=0
  count = count+1
  nextc = txbuffers %if nextc=0; nextc = nextc-1
%end

%routine intercept interrupts
%label this one
  interrupt handler = addr(this one)
  %return
thisone:
%option "-nocheck"
  *temp 0
  *move.l d0,-(sp)
  *move.w scb_status,d0
  *and.w #statusmask,d0
  %if d0#0 %start
    *move.w scb_status,intstatus
    *or.w d0,scb_command
    cxcount = cxcount+1 %if d0&cx#0
    frcount = frcount+1 %if d0&fr#0
    cnrcount = cnrcount+1 %if d0&cnr#0
    rnrcount = rnrcount+1 %if d0&rnr#0
    intcount = intcount+1
    *move.b scp_ca,d0
  %finish
  *move.l (sp),d0
  *move.l oldinterrupthandler,(sp)
  *rts
end:
%end

%routine initialise
%owninteger zero=0 {prevent CLR}
%integer prev,i
  i = addr(scp)-addr(dummy)
  write(i,0); printstring(" spare bytes"); newline
  %stopif i<0
  mem(i) = d7 %for i = 0,1,32767
  i = addr(rxbd(0))-base
  printstring("Minimum descriptor offset: "); write(i,0); newline
  %stopif i<0
  cxcount = 0; frcount = 0; cnrcount = 0; rnrcount = 0
  intcount = 0; old interrupt handler = interrupt handler
  intercept interrupts
  prev = rxbuffers-1
  %for i = 0,1,rxbuffers-1 %cycle
    rxbd(i) = 0
    rxbd(i)_next index = prev
    rxbd(i)_next offset = offset(rxbd(prev))
    rxbd(i)_buffer ad = swap4(addr(rxbuffer(i)))
    rxbd(i)_size = swap2(buffersize)
    rfd(i) = 0
    rfd(i)_link index = prev
    rfd(i)_link offset = offset(rfd(prev))
    rfd(i)_desc offset = none
    prev = i
  %repeat
!!rxbd(0)_size = rxbd(0)_size!el
!!rfd(0)_command = el
  rfd(prev)_desc offset = offset(rxbd(prev))
  nextr = prev
  prev = txbuffers-1
  %for i = 0,1,txbuffers-1 %cycle
    txbd(i) = 0
    txbd(i)_count = eof
    txbd(i)_next index = prev
    txbd(i)_next offset = offset(txbd(prev))
    txbd(i)_buffer ad = swap4(addr(txbuffer(i)))
    cmd(i) = 0
    cmd(i)_command = command transmit ! sbit
    cmd(i)_link index = prev
    cmd(i)_link offset = offset(cmd(prev))
    prev = i
  %repeat
  nextc = prev
  scb = 0
  scb_cbl offset = offset(cmd(nextc))
  scb_rfa offset = offset(rfd(nextr))
  scb_command = 0
  scb_command = cuc start
  iscp_scb base = swap4(base)
  iscp_scb offset = offset(scb)
  iscp_busy = 1
  scp_iscp ad = swap4(addr(iscp))
  scp_sysbus = zero
  com == cmd(nextc)
  com_command = command ia setup ! sbit
  com_ia = my address
  nextc = com_link index
  printstring("initialising"); newline
  channel attention
  %cycle; %repeatuntil iscp_busy#1 %or testsymbol>=0
  printstring("busy="); phex2(iscp_busy); newline
%end

%integer i
  initialise
  %cycle
    i = testsymbol
    %if i<0 %start; check interrupts
    %elseif i='.'; scb_command = reset; channel attention; %exit
    %elseif i='r'; receiveframe
    %elseif i='t'; transmitframe
    %elseif i='a'; scb_command = cuc abort ! ruc abort; channel attention
    %elseif i='s'; scb_command = cuc start ! ruc start; channel attention
    %elseif i!32='n' %or i!32='x' %or i!32='c'
      com == cmd(nextc); nextc = com_link index
      com_command = command configure ! sbit
      com_config count = 12
      com_config data(1) = 16_08
      com_config data(2) = 16_80
      com_config data(3) = 16_26
      %if i!32='n' %start
        com_config data(3) = com_config data(3)+16_40
      %elseif i!32='x'
        com_config data(3) = com_config data(3)+16_80
        seq = -2
      %else
        seq = 64
      %finish
      com_config data(4) = 16_08
      com_config data(5) = 16_60
      com_config data(7) = 16_FA
      i = 0 %if i>='a'; i = 1 %unless i=0
      com_config data(8) = i
      com_config data(10)= 16_40
      scb_command = cuc resume
      channel attention
    %elseif i='d'
      com == cmd(nextc)
      com_command = command dump ! sbit
      com_buffer offset = offset(txbuffer(nextc))
      dumpbuf == array(addr(item at offset(com_buffer offset)))
      scb_command = cuc resume
      nextc = com_link index
      channel attention
    %elseif i='e'
      %for i = 0,1,169 %cycle
        space %if i&1=0; phex2(dumpbuf(i))
        newline %if i&15=15
      %repeat; newline
    %elseif i='y'
      com == cmd(nextc); nextc = com_link index
      com_command = command diagnose ! sbit ! ibit
      scb_command = cuc resume
      channel attention
      %cycle
        %exitif testsymbol>=0 %or com_status&done#0
      %repeat
      phex4(swap2(com_status)); newline
    %elseif i='z'
      com == cmd(nextc); nextc = com_link index
      com_command = command tdr ! sbit ! ibit
      scb_command = cuc resume
      channel attention
      %cycle
        %exitif testsymbol>=0 %or com_status&done#0
      %repeat
      phex4(swap2(com_status)); space
      phex4(swap2(com_time)); newline
    %else; printsymbol(7); %finish
  %repeat
  interrupt handler = oldinterrupt handler
%end
