!spear
! SPEAR: LANCE emulator
! RWT January 1985

! The purpose of this module is to provide the same low-level
! interface to the CSD142 2MHz Ethernet controller ("SPEAR")
! as is provided by the Am7990 10MHz controller ("LANCE").
! The level of interface provided is that of access to the
! LANCE (or SPEAR) Control/Status Registers.
! For LANCE emulation of SPEAR to be possible, the access
! has to be procedural: a function to read a CSR, and a
! routine to write to a CSR.

%include "lance.inc"
%option "-low-nodiag-noline-nocheck"

! LANCE interface

@16_fe0000 %half lance address port
@16_fe0004 %half lance    data port

%ownhalf lance register = 9999

%externalintegerfn lance read csr(%half which)
  %unless lance register = which %start
    lance address port = which
    lance register = which
  %finish
  %result = lance data port
%end

%externalroutine lance write csr(%half which,value)
  %unless lance register = which %start
    lance address port = which
    lance register = which
  %finish
  lance data port = value
%end

! SPEAR INTERFACE

%ownhalfarray csr(0:3) = c stop, 0, 0, bswp  {The four CSRs
%ownrecord(ibf)%name ib                      {Pointer to Init Block
%ownrecord(drf)%name rdr,tdr                 {Pointers to descriptor rings
%ownrecord(df)%name rd,td                    {Pointers to current descriptors
%ownhalf rdn = 0, tdn = 0                    {Index (0:127) of current descs
%ownhalf r mask = 0, t mask = 0              {Number of descs in ring minus one
%owninteger old vector = 0
@16_1070 %integer hardware vector {IPL=4: physical 2M ether device
                                  {IPL=3: physical 10M ether device
@16_ff5fff %byte spear interrupt  {IPL=2: virtual 2M ether device

%routinespec write csr0(%integer value)
%routinespec install interrupt handler
%routinespec remove interrupt handler

%externalintegerfn spear read csr(%integer which)
  %result = csr(which&3)
%end

%externalroutine spear write csr(%integer which,value)
  which = which&3
  %if which#0 %start
    %returnif csr(0) & c stop = 0
    csr(which) = value %unless csr(0)&c stop#0
  %else
    write csr0(value)
  %finish
%end

! INTERNAL WORKINGS START HERE

@16_010f8 %routine ether open(%integer port,remote)
@16_03fa8 %byte ether ldte,ether lsap,ether rdte,ether rsap
@16_7fffc %byte ether status
@16_7fffd %byte ether data
@16_7ffff %byte ether control

%constbyte %c
ether reset = 16_40,
ether tbe = 8,
ether drf = 4,
ether crf = 2,
ether rrf = 1,

ether rdy = 16_10,
ether stx = 16_20,
ether dtx = 16_30,
ether ack = 16_c0,
ether etx = 16_0b,
ether transparent = 2

%routine ether twait
  %cycle; %repeatuntil etherstatus&ethertbe#0
%end

%integerfn ether dread
  %cycle; %repeatuntil etherstatus&etherdrf#0
  %result = etherdata
%end

%integerfn ether cread
  %cycle; %repeatuntil etherstatus&ethercrf#0
  %result = ethercontrol
%end

%consthalf %c
implicit clear = c err ! c intr,
explicit clear = c babl ! c cerr ! c miss ! c merr ! c rint ! c tint ! c idon,
explicit copy  = c inea,
explicit set   = c tdmd ! c stop ! c strt ! c init,
errors         = c babl ! c cerr ! c miss ! c merr,
interrupts     = c babl ! c miss ! c merr ! c rint ! c tint ! c idon

%routinespec csrset(%integer flags)

%routine writecsr0(%integer value)
  csr(0) = csr(0) %c
         & \(value & explicit clear ! implicit clear ! explicit copy) %c
         ! value & (explicit set ! explicit copy)
  %if value & c stop#0 %start  {Overrides everything else
    ether status = ether reset
    remove interrupt handler
    csr(0) = c stop
    ether status = ether crf ! ether drf
    ether open(ether lsap, ether rdte <<8! ether rsap)
    %return
  %finish
  %if csr(0) & c init # 0 %start
    ether status = ether reset
    csr(0) = csr(0) &\ c stop
    ib == record(csr(2)<<16+csr(1))
    rmask = 1<<(ib_rcount>>3)-1
    rdr == record(ib_highrad<<16+ib_lowrad)
    rdn = 0; rd == rdr_desc(rdn)
    tmask = 1<<(ib_tcount>>3)-1
    tdr == record(ib_hightad<<16+ib_lowtad)
    tdn = 0; td == tdr_desc(tdn)
    ether status = 0
    ether twait
    ether control = ether transparent
    ether twait
    install interrupt handler
    csr(0) = csr(0) &\ c rxon %unless ib_mode & m drx = 0
    csr(0) = csr(0) &\ c txon %unless ib_mode & m dtx = 0
    csr(0) = csr(0) ! c idon
  %finish
  %if csr(0) & c strt # 0 %start
    csr(0) = csr(0) ! c rxon %if ib_mode & m drx = 0
    csr(0) = csr(0) ! c txon ! c tdmd %if ib_mode & m dtx = 0
  %finish
  %if csr(0) & c tdmd # 0 %start
    ether status = ether crf ! ether tbe
  %finish
  csrset(0)
%end

%routine remove interrupt handler
  hardware vector = old vector %unless old vector = 0
  old vector = 0
%end

%routine install interrupt handler
%option "-low-nodiag-noline-nocheck"
%label entry point
%ownrecord(df)%name nextdesc
%ownbyte dump,char
%ownhalf quota
%routinespec fill buffer(%record(df)%name d)
%routinespec empty buffer(%record(df)%name d)

  %returnunless old vector = 0
  old vector = hardware vector
  *lea entry point,a0
  *move.l a4,-4(a0)
  *move.l a0,hardware vector
  %return

  *=0; *=0  {space for own base (A4)
entry point:
  *movem.l d0-d1/d4/a0/a4,-(sp)
  *lea entry point,a4
  *move.l -4(a4),a4
  *clr.l d4
  *temp d0/d1/d4/a0
  %if ether status & ether crf # 0 %start
    char = ether control
    dump = ether dread
    %if char = ether dtx %start
      ether twait; ether control = ether rdy
      ether twait; ether data = 0
    %elseif char = ether stx
      %if csr(0) & c rxon = 0 %start
        fill buffer(nil)
      %elseif rd_control & r own = 0
        fill buffer(nil)
        csrset(c miss)
      %else
        rd_control = rd_control ! r stp
        %cycle
          fill buffer(rd)
          %exitif rd_control & r enp # 0
          rdn = (rdn+1)&rmask
          nextdesc == rdr_desc(rdn)
          %if nextdesc_control & r own = 0 %start
            rd_control = (rd_control ! r buff ! r oflo) &\ r own
            rd == nextdesc
            %exit
          %finish
          rd == nextdesc
        %repeat
        rd_control = rd_control &\ r own
        csrset(c rint)
      %finish
    %elseif char = ether rdy
      ether twait; ether control = ether stx
      ether twait; ether data = 0
      quota = 512+20+14
      %cycle
        empty buffer(td)
        %exitif td_control & t enp # 0
        tdn = (tdn+1)&tmask
        nextdesc == tdr_desc(tdn)
        %if nextdesc_control & t own = 0 %start
          td_plus = d buff ! d uflo
          td_control = td_control ! t err
          td == nextdesc
          %exit
        %finish
        td == nextdesc
      %repeatuntil csr(0) & (c babl ! c inea) = c babl ! c inea
      ether twait; ether control = ether etx
      td_control = td_control &\ t own
      csrset(c tint)
    %elseif char = ether ack
      %if td_control & (t own ! t stp) = t own ! t stp %start
        ether twait; ether control = ether dtx
        ether twait; ether data = 0
      %finish
    %finish
  %elseif csr(0) & c tdmd # 0
    ether status = ether crf
    %if td_control & (t own ! t stp) = t own ! t stp %start
      ether twait; ether control = ether dtx
      ether twait; ether data = 0
    %finish
  %finish
  ether status = ether crf
  *movem.l (sp)+,d0-d1/d4/a0/a4
  *rte

%routine fill buffer(%record(df)%name d)
%half i,j
%integer p
  %if d==nil %start
    %cycle
      %if ether status & ether crf # 0 %start
        i = ether control; %return
      %finish
      %if ether status & ether drf # 0 %start
        i = ether data
      %finish
    %repeat
  %finish
  i = d_minus; j = 0
  p = d_highad<<16+d_lowad
  %cycle
    %if ether status & ether crf # 0 %start
      d_plus = j; d_control = d_control ! t enp
      i = ether control
      %return
    %finish
    %if ether status & ether drf # 0 %start
      %if i=0 %start
        d_plus = j; %return
      %finish
      i = i+1; j = j+1; byteinteger(p) = ether data; p = p+1
    %finish
  %repeat
%end

%routine empty buffer(%record(df)%name d)
%half i
%integer p
  i = d_minus
  p = d_highad<<16+d_lowad
  %while i#0 %cycle
    i = i+1; quota = quota+1
    %if quota=0 %start
      csrset(c babl)
      %returnif csr(0) & c inea # 0
    %finish
    ether twait; ether data = byteinteger(p); p = p+1
  %repeat
%end

%end

%routine csrset(%integer flags)
  *temp d0/d1/d4/a0 {NB called from interrupt handler
  csr(0) = csr(0) ! flags
  csr(0) = csr(0) ! c err %if csr(0) & errors # 0
  csr(0) = csr(0) ! c intr %if csr(0) & interrupts # 0
  %returnunless csr(0) & (c intr ! c inea) = (c intr ! c inea)
  spear interrupt = 255
%end
