/* LarkMonELoad.c
   TeleLoad server using trivial ether driver
   L. Stewart, February 11, 1983  2:21 PM, created from LarkELoad.c
   L. Stewart, February 18, 1983  7:00 PM, SendState
   L. Stewart, February 24, 1983  2:17 PM, Advice & setHost bits
   L. Stewart, March 3, 1983  2:39 PM, access to statics
   L. Stewart, April 28, 1983  11:27 AM, tl stuff to LarkMonML
   L. Stewart, April 28, 1983  11:36 AM, larger state block, CheckState
   L. Stewart, April 28, 1983  5:27 PM, Checksum
 */

#include <Ec.h>
#include <Env.h>
#include <Queue.h>
#include <Pup.h>
#include <LarkSlave.h>

/* the format of junk is
  struct Advice {
    bit setAddr;
    bit setAdvice;
    bit(14) advice;
    };
 */
struct Core {
  int advice, addrlo, addrhi, count;
  char data[1];
  };

/* from system */
extern SingleStep();
extern GoNormal();
extern GoFromBreak();
extern int mState[1];
extern int advice;

/* from runtime */
extern MoveBlock();
extern ByteBlt();
extern Swab();
extern DoubleEQ();

/* from Trivial Pup package */
extern SLCReset();
extern SStartRx();
extern struct PBI *SCheckRx();
extern SStartTx();
extern int SCheckTx();  /* BOOL */

/* local variables */
static int elBuf[130];
static struct PBI elPBI;
static int tlID[2];
static int stateTimer;
static int stateInterval;
static int gotReply;

/* next section must stay in order for GetDebugPtrs
    to work (why?) */
extern int tlNet;
extern int tlHost;
extern int tlImHost;
extern int localNet;

#define ELWrite 0x00c0
#define ELRead 0x00c2
#define ELStateWrite 0x00c4
#define ELStateRead 0x00c6
#define ELDebug 0x00c8
#define ELBooted 0x00c9

/* these are for new break and single step facilities */
#define ELSingleStep 0x00ca
#define ELGoFromBreak 0x00cc

/* read and write the slave address space */
#define ELSWrite 0x00ce
#define ELSRead 0x00d0

/* call a procedure */
#define ELCall 0x00d2

/* call ELoad to set up a TeleDebug server */
StartEL()
  {
  SLCReset();
  elPBI.pup = (struct Pup *) &elBuf[2];
  tlID[0] = tlID[1] = 0x6767;
  SStartRx(&elPBI, 256);
  };

CheckEL()
  {
  if (SCheckRx() == 0) return;
  ELoadProc(&elPBI);
  SStartRx(&elPBI, 256);
  };

static ELoadProc(pbi)
  struct PBI *pbi;
  {
  struct Pup *pup;
  char *caddr;
  char *cdata;
  int cAdvice;
  int ccount;
  int pupType;
  int maxLength;
  int dHost;
  struct Core *core;
  pup = pbi->pup;

  /* check for various bad things */
  if (pup->dPort.socket.LS != 0) goto reject;
  if (pup->dPort.socket.ms != 0x3000) goto reject;
  /* accept broadcase teledeb packets for now */
  dHost = pup->dPort.host;
  if ((dHost != EtherHost()) && (dHost != 0)) goto reject;
  if (!CheckCheckSum(pup)) goto reject;
  core = (struct Core *) &pup->data;
  if (core->addrhi) goto reject;
  ccount = Swab(core->count);
  if (ccount < 0) goto reject;
  maxLength = ((128 - pupOvWords) - 3) << 1;
  if (ccount > maxLength) {
    ccount = maxLength;
    core->count = Swab(maxLength);
    };
  caddr = Swab(core->addrlo);
  cdata = &core->data[0];
  cAdvice = Swab(core->advice);
  if (dHost == 0) cAdvice &= 0x3fff;
  core->advice = Swab(advice);
  pupType = pup->type;

  switch (pupType) {
    case ELStateWrite:
    case ELSingleStep:
    case ELGoFromBreak: 
      if (dHost == 0) goto reject;
      if (((int) caddr != 1) || (ccount != 28)) goto reject;
      ByteBlt(mState, cdata, ccount);
    case ELStateRead:
      if ((int) caddr != 1) goto reject;
      ByteBlt(cdata, mState, ccount);
      pup->length = Swab((31 + ccount) & 0xfffe);
      pup->type = pup->type + 1;
      ReplyTo(cAdvice, pbi);
      if (pupType == ELStateWrite) GoNormal();
      if (pupType == ELGoFromBreak) GoFromBreak();
      if (pupType == ELSingleStep) SingleStep();
      goto ok;
    case ELWrite:
      if (dHost == 0) goto reject;
      ByteBlt(caddr, cdata, ccount);
    case ELRead:
      if (Ugt(caddr, 0xffcf)) goto reject;
      if (Ugt(ccount, 0xffd0 - ((int) caddr))) goto reject;
      ByteBlt(cdata, caddr, ccount);
      pup->length = Swab((31 + ccount) & 0xfffe);
      pup->type = pup->type + 1;
      ReplyTo(cAdvice, pbi);
      goto ok;
    case ELDebug: {
      if (dHost != 0) CallDebugger(0xfffa);
      goto reject;
      };
    case ELSWrite:
      if (dHost == 0) goto reject;
      SlaveBLT(caddr, cdata, ccount);  /* how tell when it is done ? */
    case ELSRead:
      SlaveBLT(cdata, caddr, ccount);
      pup->length = Swab((31 + ccount) & 0xfffe);
      pup->type = pup->type + 1;
      ReplyTo(cAdvice, pbi);
      goto ok;
    case ELCall:
      CallPkt(cdata);
      pup->type = pup->type + 1;
      ReplyTo(cAdvice, pbi);
      goto ok;
    default: goto reject;
    };
  ok:
    gotReply = true;
  reject:
  };

/* Call this routine with a filled in pup which came from the
    right place.
 */

static ReplyTo(cAdvice, pbi)
  int cAdvice;
  struct PBI *pbi;
  {
  struct Pup *pup;
  struct EtherEncapsulation *ee;
  int temp;
  if (cAdvice & 0x4000) advice = cAdvice & 0x3fff;
  pup = pbi->pup;
  temp = (int) pup;
  temp -= 4;
  ee = (struct EtherEncapsulation *) temp;

  /* save Pup ID and source */
  MoveBlock(tlID, &pup->id[0], 2);
  if (cAdvice & 0x8000) {
    tlNet = pup->sPort.net;
    tlHost = pup->sPort.host;
    tlImHost = ee->src;
    };
  /* always believe net we are told we are on */
  if (pup->dPort.net != 0) localNet = pup->dPort.net;

  /* Swap source and destination */
  ee->dst = ee->src;
  ee->src = EtherHost();
  ee->type = 2;
  SwapSourceAndDest(pup);
  pup->transport = 0;
  ReallySetCheckSum(pup);
  SStartTx(pbi);
  while (!SCheckTx(pbi)) CheckWDT();
  };

SetCheckSum(pup)
  int *pup;
/* struct Pup *pup; */
  {
/*
  pup->data.words[((Swab(pup->length)-pupOvBytes)+1)>>1] = -1;
 */
  pup[((Swab(*pup) +1) >> 1) - 1] = -1;
  };

CheckCheckSum(pup)
  int *pup;
/* struct Pup *pup; */
  {
  int ck;
/*
  ck = pup->data.words[((Swab(pup->length)-pupOvBytes)+1)>>1];
 */
  ck = pup[((Swab(*pup) +1) >> 1) - 1];
  if ((ck == -1) || (ck == CheckSum(pup))) return(true);
  return(false);
  };

ReallySetCheckSum(pup)
  int *pup;
/* struct Pup *pup; */
  {
/*
  pup->data.words[((Swab(pup->length)-pupOvBytes)+1)>>1] = CheckSum(pup);
 */
  pup[((Swab(*pup) +1) >> 1) - 1] = CheckSum(pup);
  };

SwapSourceAndDest(pup)
  struct Pup *pup;
  {
  int temp[3];
  MoveBlock(temp, &pup->dPort, 3);
  MoveBlock(&pup->dPort, &pup->sPort, 3);
  MoveBlock(&pup->sPort, temp, 3);
  };

FirstSendState()
  {
  gotReply = false;
  stateInterval = 1000;
  SendState();
  };

CheckState()
  {
  if (gotReply) return;
  if (TmrExp(&stateTimer)) {
    if (stateInterval < 0) stateInterval = 1000;
    if (stateInterval >= 5000) {
      tlNet = 0;
      tlHost = 0;
      localNet = 0;
      tlImHost = 0;
      };
    if (stateInterval < 30000) stateInterval += 1000;
    SendState();
    StartEL();
    };
  };

static SendState()
  {
  struct Pup *pup;
  struct EtherEncapsulation *ee;
  int temp;
  struct Core *core;
  SLCReset();
  elPBI.pup = (struct Pup *) &elBuf[2];
  pup = elPBI.pup;
  temp = (int) pup;
  temp -= 4;
  ee = (struct EtherEncapsulation *) temp;
  ee->src = tlImHost;
  pup->sPort.net = tlNet;
  pup->sPort.host = tlHost;
  pup->dPort.net = localNet;
  pup->dPort.host = EtherHost();
  pup->sPort.socket.LS = pup->dPort.socket.LS = 0;
  pup->sPort.socket.ms = pup->dPort.socket.ms = 0x3000;
  pup->type = ELBooted;
  DoubleIncrement(tlID, 1);
  MoveBlock(&pup->id[0], tlID, 2);
  core = (struct Core *) &pup->data;
  core->count = Swab(38);
  core->addrhi = 0;
  core->addrlo = Swab(1);
  ByteBlt(&core->data[0], mState, 48);
  pup->length = Swab((31 + 48) & 0xfffe);
  ReplyTo(false, &elPBI);
  SetTmr(stateInterval, &stateTimer);
  };

int *GetELBuf()
  {
  return(elBuf);
  };

int *GetDebugPtrs()
  {
  return(&tlNet);
  };

SlaveBLT(dest, src, count)
  int dest, src, count;
  {
  StoreW(src, BltFrom);
  StoreW(dest, BltTo);
  /* works in words */
  StoreW(count >> 1, BltCount);
  StartSlave(4, 0);
  SetTmr(count, &dest);
  while (!TmrExp(&dest)) CheckWDT();
  };

/* the packet data field contains
    proc
    nargs
    arguments
    first word past end is at cdata+4+(2*nargs)
 */

struct CPArgs {
  int proc;
  int nargs;
  int returnSlot;
  int args[5];
  };

extern int stackLimit;
extern int monEnd;

CallPkt(cp)
  struct CPArgs *cp;
  {
  int stklimit;
  BlockSwab(cp, 8);
  if (cp->nargs > 5) return;
  stklimit = stackLimit;
  stackLimit = (int) &monEnd;
  stackLimit += 20;
  cp->returnSlot = Apply(&cp->args[0], cp->proc, cp->nargs);
  BlockSwab(cp, 8);
  stackLimit = stklimit;
  };

BlockSwab(ptr, count)
  int *ptr, count;
  {
  int i;
  for (i = 0; i < count; i += 1) ptr[i] = Swab(ptr[i]);
  };