/* LarkMon.c
   C monitor program for Lark
   L. Stewart February 18, 1983  6:56 PM
   L. Stewart February 23, 1983  3:16 PM, MainRet, ClockPtr
   L. Stewart February 24, 1983  1:48 PM, StatePtr
   L. Stewart February 24, 1983  1:48 PM, SetDebugHost
   L. Stewart March 3, 1983  2:24 PM, WDTBoot InitAnalog
   L. Stewart April 28, 1983  11:35 AM, open relays, persistant SendState, CallWithStack
 */

#include	"Env.h"
#include	"Lark.h"
#include	"LarkNet.h"

/* machine language assist */

extern StartM();   /* execute using register save area */
extern int mState[15];  /* register save area */
extern int monRelays;

/* StartM saves regp->ip in lastIP before starting execution */
extern int lastIP;

/* The refresh interrupt handler watches for down transitions
    of the NMI button.  lastNMI holds the previous state.
 */
extern int lastNMI;
extern int bootSwitches;
extern int rtcLow;

/* other packages */
extern StoreB();   /*  store(byte, address)  */
extern SetTmr();  /* SetTmr(interval, &timer) */
extern TmrExp();  /* if (TmrExp(&timer)) { ... } */
extern KickWDT();  /* kick watchdog timer */
extern InitAnalog();
extern GetBootSwitches();
extern GetPIO();

/* TeleLoad package */
extern FirstSendState();
extern StartEL();
extern CheckEL();

extern int tlNet;
extern int tlHost;
extern int tlImHost;
extern int localNet;

struct regs {
  int ax, bx, cx, dx;
  int sp, bp, si, di;
  int cs, ds, ss, es;
  int ip, fl, why;
  };

/* statics */
struct regs *regp;  /* register save area */
int ssCount;   /* number of tries at single stepping */
int breakSS;   /* BOOLEAN -- proceeding from a breakpoint */
int wdtTimer;  /* timer for scheduling WDT kicks */

#define RESET 0xffff
#define EXTINT 0xfffe
#define RUNERR 0xfffd
#define MONRET 0xfffc
#define SSFAIL 0xfffb
#define TeleBoot 0xfffa
#define MainRet 0xfff9
#define WDTBoot 0xfff8
#define Running 0xff00
#define TRACE 1
#define NMI 2

MonMain(why)
  int why;
  {
  regp = (struct regs *) mState;  /* use mstate later */
  PokeWDT();

  /* some reasons for booting require special treatment
      most will just be reported to the debugging machine
   */
  switch (why) {
    case WDTBoot: { InitAnalog(); break; };
    case RESET: { MonInit(); break; };
    case TRACE: { MonTrace(); break; };
    case NMI: { MonNMI(); break; };
    };
  /* save relay states in monRelays, then open relays */
  monRelays = 0;
  if (GetPIO(iapioa) & 0x0080) monRelays |= 8;  /* revert */
  if (!(GetPIO(iapioa) & 0x0040)) monRelays |= 4;  /* off hook */
  if (!(GetPIO(ipioc) & 0x0010)) monRelays |= 2; /* a relay */
  if (GetPIO(ipioc) & 0x0020) monRelays |= 1; /* revert hs relay */
  Revert(true);
  RevertHS(true);
  GoOffHook(false);
  ARelay(false);
  bootSwitches = GetBootSwitches();
  FirstSendState();
  StartEL();
  for (;;) {
    CheckState();  /* keep trying until get an answer */
    CheckWDT();
    CheckEL();
    };
  };

static MonInit()
  {
  InitAnalog();
  /* this is super ugly */
  tlHost = 0;  /* broadcast */
  tlNet = 0;  /* local */
  tlImHost = 0; /* broadcast */
  localNet = 0;  /* local */
  };

/* There will need to be three ways to begin execution
   1.  normal resumption
   2.  single step
   3.  resumption from a breakpoint

However, resumption from a breakpoint could be omitted by having
the debug machine do the single step and then put the
breakpoint instruction back
  */

SingleStep()
  {
  regp->fl = regp->fl | 0x0100;
  breakSS = 0;
  ssCount = 0;
  PokeWDT();
  StartM();
  };

GoNormal()
  {
  regp->fl = regp->fl & 0xfeff;  /* clear single step */
  breakSS = 0;
  ssCount = 0;
  PokeWDT();
  StartM();
  };

GoFromBreak()
  {
  regp->fl = regp->fl | 0x0100;  /* set single step */
  breakSS = -1;
  ssCount = 0;
  PokeWDT();
  StartM();
  };

/* the proper response to a breakpoint is to put back the
    original instruction byte and back up IP by 1
 */

static MonTrace()
  {
  /* if an interrupt is pending at the time a single step is attempted
      control will come here, but without executing the single-stepped
      instruction.
      We just keep trying for awhile.
    */
  if (regp->ip == lastIP) {
    if (ssCount >= 20) CRestart(SSFAIL);
    ssCount = ssCount + 1;
    StartM();
    };
  if (breakSS != 0) {  /* proceeding from breakpoint */
    StoreB(0xcc, lastIP);  /* replace breakpoint */
    regp->fl = regp->fl & 0xfeff; /* clear single step */
    StartM();  /* continue */
    };
  };

static MonNMI()
  {
  /* wait for button to be released */
  while(!(GetPIO(2) & 1)) CheckWDT();
  lastNMI = 0;
  };

/* Utilities */

PokeWDT()
  {
  KickWDT();
  SetTmr(200, &wdtTimer);
  };

CheckWDT()
  {
  if (TmrExp(&wdtTimer)) PokeWDT();
  };