/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by Mike Parker.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#include <sys/types.h>

#include <vax/mtpr.h>
#include <vax/pte.h>
#define LOCORE /* shut off percpu[] */
#include <vax/cpu.h>
#undef LOCORE
#include <vax/frame.h>

#include "../sys/rciext/kacomm.h"

#include "regnames.h"

extern int cpu;
extern int trapregs[];

static unsigned int curint;
static int ndigs;
static char digits[] = "0123456789abcdef";
static char curstr[256];
static int curstrlen;
static REGNAME *reg;
static int regvalue;

static getreg()
{
 int i;

 for (i=0;i<nregnames;i++)
  { if (!cistrcmp(regnames[i].name,curstr))
     { reg = &regnames[i];
       return;
     }
  }
 reg = 0;
 return;
}

readreg()
{
 switch (reg->type)
  { case REG_IPR:
       regvalue = mfpr(reg->number);
       break;
    case REG_GENERAL:
       regvalue = trapregs[reg->number];
       break;
  }
}

writereg()
{
 switch (reg->type)
  { case REG_IPR:
       mtpr(reg->number,curint);
       break;
    case REG_GENERAL:
       trapregs[reg->number] = regvalue;
       break;
  }
}

erasen(n)
int n;
{
 for (;n>0;n--)
  { printf("\b \b");
  }
}

debugger()
{
 int s;
 int forcehalt;
 int n;
 int rn;

 s = splhigh();
 forcehalt = 0;
 while (1)
  {
newprompt:
    printf("DBG> ");
getcmd:
    switch (consgetchar())
     { default:
	  printf("\n? for commands\n");
	  break;
       case '?':
	  printf("?\n\
a - fake an ack message\n\
v - get info on a virtual address\n\
t - map of page tables\n\
q - get info on Q-bus address\n\
c - call a routine (no arguments)\n\
d - dump an area of memory\n\
h - %s\n\
s - stack traceback\n\
> - write to a register\n\
< - examine a register\n\
H - halt cpu\n\
B - reboot cpu\n\
R - restart minikernel\n\
^Z - return from debugger\n\
",forcehalt?"unhalt program (let it continue)":"halt program (force an exit)");
	  break;
       case '<':
	  printf("read register (register name) ");
	  if (getstring())
	   { erasen(21);
	     goto getcmd;
	   }
	  getreg();
	  if (! reg)
	   { printf(" (no such register)\n");
	     goto newprompt;
	   }
	  if (reg->danger & REG_DANGER_FROM)
	   { char c;
	     printf(" (are you sure?) ");
	     n += 17;
	     while (1)
	      { switch (c=consgetchar())
		 { case 'n': case 'N':
		      printf("%c\n",(char)c);
		      goto newprompt;
		      break;
		   case 'y': case 'Y':
		      printf("%c",(char)c);
		      n ++;
		      break;
		   default:
		      printf("\7");
		      continue;
		      break;
		 }
		break;
	      }
	   }
	  printf("\n");
	  readreg();
	  printf("%s -> %08x\n",reg->name,regvalue);
	  break;
       case '>':
	  n = 21;
	  printf("write register (register name) ");
	  if (getstring())
	   { erasen(n);
	     goto getcmd;
	   }
	  n += curstrlen;
	  getreg();
	  if (! reg)
	   { printf(" (no such register)\n");
	     goto newprompt;
	   }
	  if (reg->danger & REG_DANGER_TO)
	   { char c;
	     printf(" (are you sure?) ");
	     n += 17;
	     while (1)
	      { switch (c=consgetchar())
		 { case 'n': case 'N':
		      printf("%c\n",(char)c);
		      goto newprompt;
		      break;
		   case 'y': case 'Y':
		      printf("%c",(char)c);
		      n ++;
		      break;
		   default:
		      printf("\7");
		      continue;
		      break;
		 }
		break;
	      }
	   }
	  printf(" (value) ");
	  n += 9;
	  if (! getint())
	   { erasen(n);
	     goto getcmd;
	   }
	  printf("\n");
	  writereg();
	  break;
       case 'a':
	  printf("ack (type) ");
	  if (! getint())
	   { erasen(11);
	     goto getcmd;
	   }
	  printf("\n");
	  sendack(curint);
	  break;
       case 'B':
	  printf("Boot cpu\n");
	  reboot();
	  break;
       case 'c':
	  printf("call (addr) ");
	  if (! getint())
	   { erasen(12);
	     goto getcmd;
	   }
	  printf("\n");
	  callit();
	  break;
       case 'd':
	  printf("dump (starting address) ");
	  n = 24;
	  rn = getint();
	  if (! rn)
	   { erasen(n);
	     goto getcmd;
	   }
	  n += rn;
	  printf(" (number of bytes) ");
	  n += 19;
	  rn = curint;
	  if (! getint())
	   { erasen(n);
	     goto getcmd;
	   }
	  printf("\n");
	  dumpmem(rn,curint);
	  break;
       case 'h':
	  forcehalt = ! forcehalt;
	  printf("h\nProgram will %sbe forced to exit when debugger returns\n",
		forcehalt?"":"not ");
	  break;
       case 'H':
	  printf("Halt cpu\n");
	  halt();
	  break;
       case 'v':
	  printf("virtual info (addr) ");
	  if (! getint())
	   { erasen(20);
	     goto getcmd;
	   }
	  printf("\n");
	  printvainfo();
	  break;
       case 't':
	  printf("table map\n");
	  printptmap();
	  break;
       case 'q':
	  printf("q-bus info (addr) ");
	  if (! getint())
	   { erasen(18);
	     goto getcmd;
	   }
	  printf("\n");
	  printqbinfo();
	  break;
       case 'R':
	  printf("Restart minikernel\n");
	  restart();
	  break;
       case 's':
	  printf("Stack traceback\n");
	  stacktrace();
	  break;
       case '\32':
	  printf("^Z\n");
	  if (forcehalt)
	   { force_exit();
	   }
	  splx(s);
	  return;
	  break;
     }
  }
}

getint()
{
 int c;
 int dvalue;

 curint = 0;
 ndigs = 0;
 while (1)
  { c = consgetchar();
    dvalue = 0;
    switch (c)
     { case '\177': case '\b':
	  if (ndigs <= 0)
	   { return(0);
	   }
	  ndigs --;
	  curint >>= 4;
	  printf("\b \b");
	  break;
       case 'f': case 'F':
	  dvalue ++;
	  /* fall through */
       case 'e': case 'E':
	  dvalue ++;
	  /* fall through */
       case 'd': case 'D':
	  dvalue ++;
	  /* fall through */
       case 'c': case 'C':
	  dvalue ++;
	  /* fall through */
       case 'b': case 'B':
	  dvalue ++;
	  /* fall through */
       case 'a': case 'A':
	  dvalue ++;
	  /* fall through */
       case '9':
	  dvalue ++;
	  /* fall through */
       case '8':
	  dvalue ++;
	  /* fall through */
       case '7':
	  dvalue ++;
	  /* fall through */
       case '6':
	  dvalue ++;
	  /* fall through */
       case '5':
	  dvalue ++;
	  /* fall through */
       case '4':
	  dvalue ++;
	  /* fall through */
       case '3':
	  dvalue ++;
	  /* fall through */
       case '2':
	  dvalue ++;
	  /* fall through */
       case '1':
	  dvalue ++;
	  /* fall through */
       case '0':
	  if (ndigs >= 8)
	   { printf("\7");
	   }
	  else
	   { printf("%c",digits[dvalue]);
	     ndigs ++;
	     curint <<= 4;
	     curint |= dvalue;
	   }
	  break;
       case '\r':
	  if (ndigs > 0)
	   { return(ndigs);
	   }
	  else
	   { printf("\7");
	   }
	  break;
       default:
	  printf("\7");
	  break;
     }
  }
}

getstring()
{
 int c;

 curstrlen = 0;
 while (1)
  { c = consgetchar();
    switch (c)
     { case '\177': case '\b':
	  if (curstrlen <= 0)
	   { return(1);
	   }
	  curstrlen --;
	  printf("\b \b");
	  break;
       case '\r':
	  curstr[curstrlen] = '\0';
	  return(0);
	  break;
       default:
	  if ((c < ' ') || (c > '~') || (curstrlen >= sizeof(curstr)-2))
	   { printf("\7");
	   }
	  else
	   { curstr[curstrlen++] = c;
	     printf("%c",(char)c);
	   }
	  break;
     }
  }
}

callit()
{
 (*((int (*)())curint))();
}

char *pte_prot(pte)
int pte;
{
 switch (pte & PG_PROT)
  { default:
       return("?");
       break;
#define CASE(name) case PG_/**/name: return("name"); break;
    CASE(NOACC);
    CASE(KW);
    CASE(KR);
    CASE(UW);
    CASE(URKW);
    CASE(URKR);
  }
}

printvainfo()
{
 int br;
 int lr;
 int vpn;
 int pte;

 vpn = (curint & 0x3fffffff) >> 9;
 printf("%x: ",curint);
 switch (curint & 0xc0000000)
  { case 0x00000000:
       br = mfpr(P0BR);
       lr = mfpr(P0LR);
       printf("P0 address: BR=%x LR=%x: ",br,lr);
       if (vpn >= lr)
	{ printf("out of segment\n");
	}
       else
	{ if (cpu == VAX_620)
	   { pte = 0x80000000 | (br+(vpn<<2));
	   }
	  else
	   { pte = br + (vpn<<2);
	   }
	  if (badaddr(pte,4))
	   { printf("pte reference (%x) fails\n",pte);
	   }
	  else
	   { pte = * (int *) pte;
	     printf("pte=%x",pte);
	     if (pte & PG_V)
	      { printf(" (pfn %x)\n",pte&PG_PFNUM);
		printf("  phys %x prot %s contents %08x\n",
				((pte&PG_PFNUM)<<9)|(curint&511),
				pte_prot(pte),
				*(int *)curint );
	      }
	     else
	      { printf(" (V clear)\n");
	      }
	   }
	}
       break;
    case 0x40000000:
       br = mfpr(P1BR);
       lr = mfpr(P1LR);
       printf("P1 address: BR=%x LR=%x: ",br,lr);
       if (vpn < lr)
	{ printf("out of segment\n");
	}
       else
	{ if (cpu == VAX_620)
	   { pte = 0x80000000 | (br+(vpn<<2));
	   }
	  else
	   { pte = br + (vpn<<2);
	   }
	  if (badaddr(pte,4))
	   { printf("pte reference (%x) fails\n",pte);
	   }
	  else
	   { pte = * (int *) pte;
	     printf("pte=%x",pte);
	     if (pte & PG_V)
	      { printf(" (pfn %x)\n",pte&PG_PFNUM);
		printf("  phys %x prot %s contents %08x\n",
				((pte&PG_PFNUM)<<9)|(curint&511),
				pte_prot(pte),
				*(int *)curint );
	      }
	     else
	      { printf(" (V clear)\n");
	      }
	   }
	}
       break;
    case 0x80000000:
       br = mfpr(SBR);
       lr = mfpr(SLR);
       printf("System address: BR=%x LR=%x: ",br,lr);
       if (vpn >= lr)
	{ printf("out of segment\n");
	}
       else
	{ pte = 0x80000000 | (br+(vpn<<2));
	  if (badaddr(pte,4))
	   { printf("pte reference (%x) fails\n",pte);
	   }
	  else
	   { pte = * (int *) pte;
	     printf("pte=%x",pte);
	     if (pte & PG_V)
	      { printf(" (pfn %x)\n",pte&PG_PFNUM);
		printf("  phys %x prot %s contents %08x\n",
				((pte&PG_PFNUM)<<9)|(curint&511),
				pte_prot(pte),
				*(int *)curint );
	      }
	     else
	      { printf(" (V clear)\n");
	      }
	   }
	}
       break;
    case 0xc0000000:
       printf("Reserved address space\n");
       break;
  }
}

printptmap()
{
 printf("Not yet implemented\n");
}

printqbinfo()
{
 int qbpage;
 extern int *Qbusmapregs;
 extern char *Qbus;
 int qbmr;
 unsigned short int *qaddr;

 if ((unsigned int)(curint-KA_QBUSBASE) > KA_QBUSSIZE)
  { printf("%x: not a Q-bus address\n",curint);
    return;
  }
 qbpage = (curint - KA_QBUSBASE) >> 9;
 qbmr = Qbusmapregs[qbpage];
 printf("%x: Q-page %x, map %x ",curint,qbpage,qbmr);
 if (qbmr & KA_QMAP_V)
  { printf("(pfn %x): phys %x\n",
	qbmr&~KA_QMAP_V,((qbmr&~KA_QMAP_V)<<9)|(curint&511));
  }
 else
  { printf("(V clear)\n");
  }
 qaddr = (unsigned short int *) &Qbus[curint-KA_QBUSBASE];
 if (badaddr(qaddr,2))
  { printf("  no Q-bus response\n");
  }
 else
  { printf("  contents %04x\n",0xffff&(int)*qaddr);
  }
}

int oos(a)
int a;
{
 switch (a & 0xc0000000)
  { case 0x00000000:
       return((a>>9)>=mfpr(P0LR));
       break;
    case 0x40000000:
       return(((a-0x40000000)>>9)<mfpr(P1LR));
       break;
    case 0x80000000:
       return(((a-0x80000000)>>9)>=mfpr(SLR));
       break;
    case 0xc0000000:
       return(1);
       break;
  }
}

int inaccessible(a,nb)
int a;
int nb;
{
 return(oos(a)||oos(a+nb-1)||badaddr(a,nb));
}

stacktrace()
{
 register int fp; /* r11 */
 register int ap; /* r10 */

 asm("movl fp,r11");
 ap = *(int *)(fp+8);
 fp = *(int *)(fp+12);
 while (1)
  { printf("\nfp = %08x, ap = %08x\n",fp,ap);
    printf("    handler = ");
    print_ba(fp,"%08x");
    printf("\n");
    printf("    bits = ");
    print_ba(fp+4,"%08x");
    printf("\n");
    printf("    ap = ");
    print_ba(fp+8,"%08x");
    printf("\n");
    printf("    fp = ");
    print_ba(fp+12,"%08x");
    printf("\n");
    printf("    pc = ");
    print_ba(fp+16,"%08x");
    printf("\n");
    if (inaccessible(ap,4))
     { printf("    bad arglist\n");
     }
    else
     { int na;
       int *argp;
       argp = (int *) ap;
       na = 0xff & *argp++;
       printf("    %d args\n",na);
       for (;na>0;na--)
	{ if (inaccessible((int)argp,4))
	   { printf("        bad arglist\n");
	     break;
	   }
	  else
	   { printf("        %08x\n",*argp++);
	   }
	}
     }
    if (inaccessible(fp+8,4) || inaccessible(fp+12,4)) break;
    ap = *(int *)(fp+8);
    fp = *(int *)(fp+12);
  }
}

print_ba(addr,format)
int addr;
char *format;
{
 if (inaccessible(addr,4))
  { printf("(bad)");
  }
 else
  { printf(format,*(int *)addr);
  }
}

dumpmem(addr,nbytes)
int addr;
int nbytes;
{
 printf("memory dump not yet implemented\n");
}
