!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