/* etherload.c   April 22, 1982  4:38 PM   */

extern char mstate[26];
extern slcinit();
extern waitccb();
extern swab();
extern blt();
extern startm();
extern sltstart();
extern slrstart();
extern chav();

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 */

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

/*  Pup download server */

etherload(host)
  int host;
  {
  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;
  slcinit(host);
  for (;;) {
    startrx();
    while (!(inccb.stata & 0x80)) if (chav()) return;
    if (inccb.stata != 0x88) continue;
    if (inccb.counta == 0) length = 256;
    else length = inccb.counta;
    echo(length);
    };
  };

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

  /* generate a delay */
  plen = 3000;
  while (--plen);

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

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

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

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

  /* copy packet accross */
  blt(rbuf, buf, 260);
  valid = 0;
  rv = 0;
  caddr = swab(tcore->addrlo);
  ccount = swab(tcore->count);
  if (tcore->addrhi) return;
  /* 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)) return;
    caddr = mstate;
    mcomc();
    };

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

  if (!rv) return(0);

  setuppkt();
  sendpkt();
  };

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

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

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

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;
  };

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

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

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