/* etherload.c
   L. Stewart September 10, 1982  11:12 AM
   L. Stewart December 3, 1982  2:19 PM, changed to poll style
 */

/* in cmon.c */
extern char mstate[26];
extern startm();

/* in cmruntimeml.dsm */
extern swab();
extern blt();

/* in cmslcml.dsm */
extern slcinit();
extern waitccb();
extern sltstart();
extern slrstart();

struct ccb {
  char cmda;
  char *addra;
  char counta;
  char stata;
  };

struct port {
  char net, host;
  int  soc1, soc2;
  };

struct pup {
  int  eaddr;
  int  etype;
  int  len;
  char tpc, type;
  int  id1, id2;
  struct port dport, sport;
  char data[1];
  };

struct core {
  int addrlo, addrhi, count;
  char data[1];
  };

struct ccb minccb, inccb, outccb;

  /* really want length to be unsigned, but *char
    is ok too.  The problem is to avoid sign extension
    when assigning from inccb.counta to length */

static char *cdata;
static char *caddr;
static char *length;
static int ccount;
static struct pup *rpup;
static struct pup *tpup;
static struct core *tcore;
static int lastid1;
static int lastid2;
static int tplen;
static int rv;
static int valid;
static int rbuf[130];
static int buf[130];
static int lhost;

/*  Pup download server */

StartEL()
  {
  lhost = GetPIO(1);
  slcinit(lhost);
  minccb.cmda = 0x00;
  minccb.addra = rbuf;
  minccb.counta = 0;
  minccb.stata = 0;
  tpup = (struct pup *) buf;
  tcore = (struct core *) tpup->data;
  cdata = tcore->data;
  rpup = (struct pup *) rbuf;
  startrx();
  };

CheckEL()
  {
  if (!(inccb.stata & 0x80)) return;
  if (inccb.stata != 0x88) StartEL();
  if (inccb.counta == 0) length = 256;
  else length = inccb.counta;
  echo(length);
  StartEL();
  };

static echo(leng)
  int leng;
  {
  int plen;
  if ((!(rpup->eaddr & 0xff)) || (leng&01) || (leng<26)) goto quitel;

  /* unswabbed etype should be 2 */
  if (rpup->etype != 2) goto quitel;

  /* check lengths */
  plen = (swab(rpup->len)+5) & 0xfffe;
  if (plen != leng) goto quitel;

  /* check pup destintion */
  if (rpup->dport.host != lhost) goto quitel;
  if ((rpup->dport.soc1) || (rpup->dport.soc2 != 0x3000)) goto quitel;
  if ((rpup->type != 0xc0) && (rpup->type != 0xc2)) {
    if ((rpup->type != 0xc4) && (rpup->type != 0xc6)) goto quitel;
    };

  if ((rpup->id1 == lastid1) && (rpup->id2 == lastid2)) {
    if (valid) sendpkt();
    goto quitel;
    };

  /* copy packet accross */
  blt(rbuf, buf, 260);
  valid = 0;
  rv = 0;
  caddr = swab(tcore->addrlo);
  ccount = swab(tcore->count);
  if (tcore->addrhi) goto quitel;
  /* check puptype */
  if (tpup->type == 0xc0) {  /* core store */
    blt(cdata, caddr, ccount);
    blt(caddr, cdata, ccount);
    comtc();
    tplen = leng;
    };

  if (tpup->type == 0xc2) {  /* core fetch */
    mcomc();
    };

  if (tpup->type == 0xc6) {  /* state fetch */
    if (((int) caddr != 1) || (ccount != 28)) goto quitel;
    caddr = mstate;
    mcomc();
    };

  if (tpup->type == 0xc4) {  /* state store */
    if (((int) caddr != 1) || (ccount != 28)) goto quitel;
    caddr = mstate;
    blt(cdata, caddr, ccount);
    mcomc();
    setuppkt();
    sendpkt();
    startm();
    };

  if (!rv) goto quitel;

  setuppkt();
  sendpkt();
  quitel:
  };

static mcomc()
  {
  blt(caddr, cdata, ccount);
  tplen = (33+ccount) & 0xfffe;
  tpup->len = swab(tplen - 4);
  comtc();
  };

static sendpkt()
  {
  outccb.cmda = 0x50;
  outccb.addra = buf;
  outccb.counta = tplen;
  outccb.stata = 0;

  sltstart(&outccb);
  /* wait for transmit done */
  waitccb(&outccb);
  };

static setuppkt()
  {
  /* swap src and destination */
  ssd(buf);

  /* fill in no-checksum */
  buf[(tplen/2)-1] = 0xffff;

  /* swap ether source and destination */
  tpup->eaddr = swab(tpup->eaddr);

  /* zero transport control */
  tpup->tpc = 0;

  /* set puptype */
  /* tpup->type += 1; doesn't work */
  tpup->type = tpup->type+1;
  };

static comtc()
  {
  rv = 1;
  valid = 1;
  lastid1 = tpup->id1;
  lastid2 = tpup->id2;
  };

static ssd(p)
  char *p;
  {
  char temp[6];
  blt(&p[12],temp, 6);
  blt(&p[18],&p[12], 6);
  blt(temp,&p[18], 6);
  };

static startrx()
  {
  if (slrstat() & 0x000c) slrinit();
  blt(&minccb, &inccb, 5);
  slrstart(&inccb);
  };