/* cmon.c
   C monitor program for Lark
   L. Stewart September 24, 1982  10:27 AM
   L. Stewart December 2, 1982  7:55 PM, invert NMI I/O bit
 */

/* machine language assist */
extern setup();    /* setup I/O controllers. */
extern putchar();   /* put character to console */
extern putcrchar();   /* put character to console */
extern getchar();   /* return character from console */
extern chav();    /* returns true if getchar won't block */
extern outbyte();   /* outbyte(port, byte) */
extern inbyte();   /* read byte from io device */
extern fetchb();    /* return byte at address */
extern storeb();   /*  store(byte, address)  */
extern portstr();  /* load IO registers from command string */

/* monitor machine language assist */
extern dump();
/*
extern tslc();
 */
extern startm();   /* execute using register save area */
extern int mstate[14];  /* register save area */
extern int lip;
extern int lnmi;
extern int eitype;

/* c runtime library */
extern int isdigit();   /* is character IN [0..9] */
extern lc();		/* lower case */
extern int eqstr();	/* case-insensitive string matcher */
extern wf();
extern wfcr();
extern wf1();
extern wf2();
extern ssenable();
extern ssreturn();

/* other packages */
extern InitAnalog();
extern GetPIO();
extern int stframe;
extern int stbyte;
extern loadhex();
/*
extern setspeed();
 */
extern int cmklo;
extern int cmkhi;

/* statics */
char cmd;
char leftover;
int any;
int mlerr[3];
int result;
char *rnary[14];
/* breakpoint */
int brkadr;
char brkcb;
int sscount;
int audb;

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

struct regs *regp;

#define RESET 255
#define BREAK 3
#define TRACE 1
#define NMI 2
#define DIVERR 0
#define OVERR 4
#define EXTINT 254

main(why)
  int why;
  {
  /* execution comes here on system reset with why=0 */
  switch (why) {
    case BREAK: { monbrk(); break; };
    case TRACE: { montr(); break; };
    case NMI: { monnmi(); break; };
    case DIVERR: { monde(); break; };
    case OVERR: { monov(); break; };
    case EXTINT: { monei(); break; };
    default: { moninit(); break; };
    };
  regp = (struct regs *) mstate;  /* use mstate later */
  /* we use NMI for refresh, and the nmi pushbutton is 
     connected to a parallel port bit, which refresh tests */
  /*  (someday) */
  for (;;) {
    result = ssenable(mlerr);
    if (result) wf(" ##\r");
    wf("% ");
    StartEL();
    cmd = lc(getchar());
    switch (cmd) {
      case 'a': { togaudio(); break; };
      case 'b': { setbrk(); break; };
      case 'c': { clock(); break; };
      case 'd': { dump(0); break; };
      case 'e': { elcmd(); break; };
      case 'f': { TraceStack(regp->bp); break; };
      case 'g': { gocmd(); break; };
      case 'i': { incmd(); break; };
      case 'n': { sscount = 0; sings(); break; };
      case 'o': { outcmd(); break; };
      case 'r': { readcmd(); break; };
      case 's': { subst(); break; };
      case 't': { tslc(); break; };
      case 'w': { dump(1); break; };
      case 'x': { regcmd(); break; };
      default: { unknown(); break; };
      };
    };
  };

moninit()
  {
  setup();  /* start refresh, in particular */
  datainit();  /* register names */
  InitAnalog();
  wf("Lark Monitor\r");
  testbrk();
  brkadr = 0;  /* no breakpoint set */
  audb = 0;
  };

monbrk()
  {
  wf1("\r Break at %04x\r", regp->ip-1);
  if (regp->ip != brkadr+1) wf("unknown breakpoint!\r");
  else {  /* put back code byte and back up ip */
    storeb(brkcb, brkadr);
    regp->ip = brkadr;
    };
  };

montr()
  {
  if (brkadr) {  /* proceeding from breakpoint */
    storeb(0xcc, brkadr);  /* replace breakpoint */
    regp->fl = regp->fl & 0xfeff;
    startm();  /* continue */
    };
  if ((sscount < 20) && (regp->ip == lip)) {
    sscount = sscount + 1;
    sings();
    };
  wf1(" ss at %04x", regp->ip);
  if (sscount > 1) wf1(", %d tries", sscount);
  wfcr();
  };

monnmi()
  {
  wf1("\rNMI at %04x\r", regp->ip);
  while(!(GetPIO(2) & 1));
  lnmi = 0;
  };

monov()
  {
  wf1("\rOverflow at %04x\r", regp->ip);
  };

monei()
  {
  wf1("\rExternal interrupt, type %x\r", eitype);
  };

monde()
  {
  wf1("\rDivide Error at %04x\r", regp->ip);
  };

setbrk()
  {
  int new;
  testbrk();
  wf("Break at: ");
  brkadr = gethex();
  innew('\r');
  testbrk();
  };

testbrk()
  {
  if (brkadr != 0 && fetchb(brkadr) == 0xcc)
    wf2("\r breakpoint at %04x! code might be %02x\r", brkadr, brkcb);
  };

clock()
  {
  wf2("Clock = %04x%04x\r", cmkhi, cmklo);
  };

elcmd()
  {
  wf1("EtherLoad, host %02x\r", GetPIO(1) & 0xff);
  StartEL();
  };

readcmd()
  {
  putchar('R');
  loadhex();
  regp->cs = stframe;
  regp->ip = stbyte;
  wfcr();
  };

sings()  /* single step */
  {
  testbrk();
  brkadr = 0;
  regp->fl = regp->fl | 0x0100;
  startm();
  };

gocmd()
  {
  wf("GO!\r");
  regp->fl = regp->fl & 0xfeff;  /* clear single step */
  if (brkadr) {
    testbrk();
    brkcb = fetchb(brkadr);
    if (brkadr == regp->ip) regp->fl = regp->fl | 0x0100;
    else storeb(0xcc, brkadr);
    };
  startm();
  };

unknown()
  {
  wf(" ??\r");
  };

mlabort()
  {
  ssreturn(mlerr, 1);
  };

debug(s)
  char *s;
  {
  wf1("\rDebug: %s\r", s);
  mlabort();
  };

int innewp(c)  /* invalid number not ending with */
  char c;
  {
  if (!any || leftover != c) return(1);
  return(0);
  };

innew(c)
  char c;
  {
  if (innewp(c)) mlabort();
  };

regcmd()
  {
  int i;
  char name[3];
  name[2] = 0;
  putchar('X');
  name[0] = egetchar();
  if (leftover == '\r') { printregs(); return; };
  name[1] = egetchar();
  for (i = 0; i < 14; i += 1)
    if (eqstr(rnary[i], name)) { modreg(i); return; };
  mlabort();
  };

modreg(i)
  int i;
  {
  int newval;
  wf1("=%04x -", mstate[i]);
  newval = gethex();
  innew('\r');
  mstate[i] = newval;
  };

printregs()
  {
  prseven(0);
  prseven(7);
  };

prseven(start)
  int start;
  {
  int i;
  for (i = start; i < (start+7); i += 1) printreg(i);
  wfcr();
  };

printreg(i)
  int i;
  {
  wf2(" %s=%04x", rnary[i], mstate[i]);
  };

incmd()
  {
  int port;
  int first;
  putchar('I');
  first = 1;
  port = gethex();
  if (!any) mlabort()
  for (;;) {
    if (!first && innewp(',')) break;
    wf1("\r%02x", inbyte(port));
    if (leftover=='\r') break;
    first = 0;
    egetchar();
    };
  if (first && (leftover=='\r')) wfcr();
  };

int egetchar()  /* getchar with echo */
  {
  leftover = getchar() & 0177;
  putcrchar(leftover);
  if (leftover == 0177) mlabort();
  return(leftover);
  };

outcmd()
  {
  int port, val;
  putchar('O');
  port = gethex();
  innew(',');
  for (;;) {
    val = gethex();
    if (innewp(',') && innewp('\r')) mlabort();
    outbyte(port, val)
    if (leftover == '\r') break;
    wf("\r- ");
    };
  };

subst()
  {
  int adr, val;
  putchar('S');
  adr = gethex();
  if (innewp(',')) return;
  for (;;) {
    val = fetchb(adr);
    wf1(" %02x- ", val);
    val = gethex();
    if (leftover != ',' && leftover != '\r') mlabort();
    if (any) storeb(val, adr);
    adr += 1;
    if (leftover == '\r') return;
    else wfcr();
    wf1("%04x- ", adr);
    };
  };

int gethex()
  {
  int v;
  v = 0;
  any = 0;
  for (;;) {
    egetchar();
    if (isdigit(leftover)) {
      v = (v<<4);
      v += (leftover-'0');
      any = 1;
      continue;
      };
    leftover = lc(leftover);
    if (leftover>='a' && leftover<='f') {
      v = (v<<4);
      v += (leftover-'a'+10);
      any = 1;
      continue;
      };
    break;
    };
  return(v);
  };

datainit()
  {
  rnary[0] = "AX";
  rnary[1] = "BX";
  rnary[2] = "CX";
  rnary[3] = "DX";
  rnary[4] = "SP";
  rnary[5] = "BP";
  rnary[6] = "SI";
  rnary[7] = "DI";
  rnary[8] = "CS";
  rnary[9] = "DS";
  rnary[10] = "SS";
  rnary[11] = "ES";
  rnary[12] = "IP";
  rnary[13] = "FL";
  };