/*
 * commands.c
 *
 * MILO debugger/monitor commands
 *
 * This file is subject to the terms and conditions of the
 * GNU General Public License. See the file COPYING in the
 * main directory of this archive for more details.
 *
 * Copyright (C) 1995 by Andreas Busse
 */
#include <stand/debug.h>

#include <stand/stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <arc/arc.h>
#include <asm/mipsregs.h>

/* local prototypes */

unsigned get_memory(unsigned caddr, unsigned width);
char *atob(char *cp, unsigned *iptr);
void showchar(unsigned c);

/*
 * dump()
 * dump memory
 */
dump(int argc, char **argv)
{
  unsigned address1 = 0x0;
  unsigned address2 = 0x0;
  unsigned val;
  int width = SW_WORD;
  
  argv++;
  argc--;
#if DEBUG  
  printf("argc=%d, argv[0]=%s\r\n",argc,argv[0]);
#endif  
  if (argc >= 1 && argv[0][0] == '-')
    {
      switch (argv[0][1])
	{
	case 'h':
	  width = SW_HALFWORD;
	  break;
	case 'w':
	  width = SW_WORD;
	  break;
	case 'b':
	  width = SW_BYTE;
	  break;
	default:
	  fprintf(stderr, "Illegal size: %s\r\n",argv[0]);
	  return 1;
	}
      argv++;
      argc--;
    }

#if DEBUG  
  printf("argc=%d, argv[0]=%s\r\n",argc,argv[0]);
#endif  
  if (!argc)
    {
      fprintf(stderr,"Missing argument(s)\r\n");
      return 1;
    }

  /* must be an address, get start address */
      
  if (*atob(*argv, &address1))
    {
      fprintf(stderr,"Illegal address: %s\n\r", *argv);
      return 1;
    }
  argc--;
  argv++;

  if (argc)			/* more arguments, get end address */
    {
      if (*atob(*argv, &address2))
	{
	  fprintf(stderr,"Illegal address: %s\n\r", *argv);
	  return 1;
	}
      argc--;
      argv++;
    }

  if (argc)
    {
      fprintf(stderr,"Illegal argument: %s\n\r", *argv);
      return 1;
    }
      
  if (!address2)		/* end address not given, so we assume +0x100 */
    address2 = address1 + 0x100;
  if (address2 <= address1)
    {
      fprintf(stderr,"Illegal address range\r\n");
      return 1;
    }

#if DEBUG
  printf("address1 = 0x%08x\r\n",address1);
  printf("address2 = 0x%08x\r\n",address2);
#endif
  
  do_dump(address1,address2,width);
  return 0;
}

/*
 * do_dump() -- Readout memory and display
 */
int do_dump(unsigned address1, unsigned address2, int width)
{
  unsigned val;
  char c;
  int i;
  
  address1 &= ~(width-1);
  address2 &= ~(width-1);
  address2 += width;
  
  while(address1 < address2)
    {
      printf("0x%08x:  ",address1);
      for (i=0; i<16; i+=width)
	{
	  val = get_memory(address1+i,width);
	  switch(width)
	    {
	    case SW_BYTE:
	      printf("%02x ",val);
	      break;
	    case SW_HALFWORD:
	      printf("%04x ",val);
	      break;
	    case SW_WORD:
	      printf("%08x ",val);
	      break;
	    }
	}
      printf("  ");
      for (i=0; i<16; i++)
	{
	  c = get_memory(address1+i,1);
	  showchar(c);
	}

      printf("\r\n");
      address1 += i;
    }
}

unsigned get_memory(unsigned caddr, unsigned width)
{
  switch (width)
    {
    case SW_BYTE:
      return (*(unsigned char *)caddr);
    case SW_HALFWORD:
      return (*(unsigned short *)caddr);
    case SW_WORD:
      return(*(unsigned *)caddr);
    default:
      printf("get_memory: Illegal width\r\n");
    }
}

void showchar(unsigned c)
{
  if (c >= 32 && c <= 127)
    printf("%c",c);
  else
    printf("_");
}

/*
 * fill() -- Fill memory 
 */
int fill(int argc, char *argv[])
{
  unsigned address1;
  unsigned address2;
  unsigned val;
  int width = SW_WORD;

  argv++;
  argc--;
  if (argc >= 1 && argv[0][0] == '-')
    {
      switch (argv[0][1])
	{
	case 'h':
	  width = SW_HALFWORD;
	  break;
	case 'w':
	  width = SW_WORD;
	  break;
	case 'b':
	  width = SW_BYTE;
	  break;
	default:
	  fprintf(stderr, "Illegal size: %s\r\n",argv[0]);
	  return 1;
	}
      argv++;
      argc--;
    }

  if (argc < 2)
    {
      fprintf(stderr, "Missing argument(s)\r\n");
      return 1;
    }
      
  /* must be an address, get start address */
      
  if (*atob(*argv, &address1))
    {
      fprintf(stderr,"Illegal address: %s\n\r", *argv);
      return(1);
    }
  argc--;
  argv++;

  if (argc > 1)			/* more arguments, get end address */
    {
      if (*atob(*argv, &address2))
	{
	  fprintf(stderr,"Illegal address: %s\n\r", *argv);
	  return(1);
	}
      argc--;
      argv++;
    }

     
  if (!address2)		/* end address not given, so we assume +width */
    address2 = address1 + width;
  if (address2 <= address1)
    {
      fprintf(stderr,"Illegal address range\r\n");
      return(1);
    }

#if DEBUG  
  printf("address1 = 0x%08x\r\n",address1);
  printf("address2 = 0x%08x\r\n",address2);
#endif
  
  if (argc > 1)
    {
      fprintf(stderr,"Illegal argument: %s\n\r", *argv[1]);
      return(1);
    }
  else if (!argc)
    {
      fprintf(stderr,"Missing argument\r\n");
      return(1);
    }
	
  if (*atob(*argv, &val))
    {
      fprintf(stderr,"Invalid argument: %s\r\n", *argv);
      return(1);
    }
#if DEBUG
  printf("val = 0x%08x\r\n",val);
#endif
  
  do_fill(address1,address2,width,val);
  return(0);
}

int do_fill(unsigned address1, unsigned address2, int width, unsigned val)
{
  address1 &= ~(width-1);
  address2 &= ~(width-1);
  address2 += width;
  
  while(address1 < address2)
    {
      switch(width)
	{
	case SW_BYTE:
	  *((char *)address1) = val;
	  break;
	case SW_HALFWORD:
	  *((short *)address1) = val;
	  break;
	case SW_WORD:
	  *((int *)address1) = val;
	  break;
	}
      address1 += width;
    }
  return(0);
}

/*
 * strings()
 * Grovel through the range of memory for strings.
 */
strings(int argc, char **argv)
{
  unsigned address1 = 0x0;
  unsigned address2 = 0x0;
  unsigned val;
  int len=4;
  
  argv++;
  argc--;
#if DEBUG  
  printf("argc=%d, argv[0]=%s\r\n",argc,argv[0]);
#endif  
  if (argc >= 1 && argv[0][0] == '-')
    {
      atob( argv[0]+1, &len );
      argv++;
      argc--;
    }

#if DEBUG  
  printf("argc=%d, argv[0]=%s\r\n",argc,argv[0]);
#endif  
  if (!argc)
    {
      fprintf(stderr,"Missing argument(s)\r\n");
      return 1;
    }

  /* must be an address, get start address */
      
  if (*atob(*argv, &address1))
    {
      fprintf(stderr,"Illegal address: %s\n\r", *argv);
      return 1;
    }
  argc--;
  argv++;

  if (argc)			/* more arguments, get end address */
    {
      if (*atob(*argv, &address2))
	{
	  fprintf(stderr,"Illegal address: %s\n\r", *argv);
	  return 1;
	}
      argc--;
      argv++;
    }

  if (argc)
    {
      fprintf(stderr,"Illegal argument: %s\n\r", *argv);
      return 1;
    }
      
  if (!address2)		/* end address not given, so we assume +0x100 */
    address2 = address1 + 0x100;
  if (address2 <= address1)
    {
      fprintf(stderr,"Illegal address range\r\n");
      return 1;
    }

#if DEBUG
  printf("address1 = 0x%08x\r\n",address1);
  printf("address2 = 0x%08x\r\n",address2);
#endif
  
  do_strings(address1,address2,len);
  return 0;
}
/*
 * do_strings() -- Readout memory and display
 */
int do_strings(unsigned address1, unsigned address2, int len)
{
  unsigned val;
  char buffer[1024];
  int match;
  
  *buffer = '\0';
  match = 0;
  while(address1 < address2)
    {
      val = get_memory(address1,1);
      if (val >= 32 && val < 127)	/* isprint(val) more or less */
	  buffer[match++] = (char) val;
      else
        {
	  if (*buffer && match > len)
	    {
	      buffer[match] = '\0';
	      printf("0x%08x: %s\r\n", address1 - match, buffer );
	    }
	  match = 0;
	}
      address1++;
    }
}

/*
 * probe()
 * Probe the TLB for a certain virtual address
 * Syntax: probe vaddr
 * FIXME: limited to 32 bit 
 */
int probe(argc, argv)
     int argc;
     char **argv;
{
  int vaddr;
  int index;
  unsigned long long entry_hi;
  unsigned long long entry_lo0;
  unsigned long long entry_lo1;
  
  if (argc < 2)
    {
      fprintf(stderr,"Missing argument\r\n");
      return(1);
    }
  if (argc > 2)
    {
      fprintf(stderr,"Illegal argument: %s\r\n",argv[2]);
      return(1);
    }
  argc--;
  argv++;
  
  if (*atob(*argv, (unsigned *)&vaddr))
    {
      fprintf(stderr,"Illegal address: %s\n\r", *argv);
      return 1;
    }

  entry_hi = (long long)vaddr;	/* sign-extend to 64 bit */

  printf("VADDR %08x:%08x:\t",(unsigned int)(entry_hi >> 32),
	 (unsigned int) entry_hi);
  
  /* probe the TLB for VADDR */
  
  write_64bit_cp0_register(CP0_ENTRYHI,entry_hi);
  __asm__ __volatile__("nop;nop;nop;nop");
  __asm__ __volatile__("tlbp");
  __asm__ __volatile__("nop;nop;nop;nop");
  index = read_32bit_cp0_register(CP0_INDEX);

  /* print the result. */
  
  if (index < 0)
    {
      printf("not mapped.\r\n");
      return(0);
    }

  printf("mapped by TLB entry %d:\r\n\r\n",index);
  
  write_32bit_cp0_register(CP0_INDEX, index);
  __asm__ __volatile__("nop;nop;nop;nop");
  __asm__ __volatile__("tlbr");
  __asm__ __volatile__("nop;nop;nop;nop");
  entry_hi = read_64bit_cp0_register(CP0_ENTRYHI);
  entry_lo0 = read_64bit_cp0_register(CP0_ENTRYLO0);
  entry_lo1 = read_64bit_cp0_register(CP0_ENTRYLO1);
  
  printf("EntryHi : %08x:%08x\t",
	 (unsigned int)(entry_hi >> 32), (unsigned int) entry_hi);
  printf("(R=%1x VADR=%02x:%08x ASID=%04x)\r\n",
	 entry_hi >> 62,
	 entry_hi & 0xff00000000ULL,
	 entry_hi & 0xffffe000ULL,
	 entry_hi & 0xffULL);
  printf("EntryLo0: %08x:%08x\t",
	 (unsigned int)(entry_lo0 >> 32), (unsigned int) entry_lo0);
  printf("(PADR=%02x:%08x C=%d D=%d V=%d G=%d)\r\n",
	 (entry_lo0 & 0x3c000000) >> 25,
	 (entry_lo0 & 0x03ffffc0) << 6,
	 (entry_lo0 & 0x00000038) >> 3,
	 (entry_lo0 & 0x00000004) >> 2,
	 (entry_lo0 & 0x00000002) >> 1,
	 (entry_lo0 & 0x00000001));
  printf("EntryLo1: %08x:%08x\t",
	 (unsigned int)(entry_lo1 >> 32), (unsigned int) entry_lo1);
  printf("(PADR=%02x:%08x C=%d D=%d V=%d G=%d)\r\n",
	 (entry_lo1 & 0x3c000000) >> 25,
	 (entry_lo1 & 0x03ffffc0) << 6,
	 (entry_lo1 & 0x00000038) >> 3,
	 (entry_lo1 & 0x00000004) >> 2,
	 (entry_lo1 & 0x00000002) >> 1,
	 (entry_lo1 & 0x00000001));
  printf("PageMask: %08x\r\n\r\n",
	 read_32bit_cp0_register(CP0_PAGEMASK));
  
  return(0);
}

/*
 * dumptlb()
 * Dump the TLB, entry by entry
 */
#define VALID 0x02ULL		/* valid bit */
#define TLBPATH "multi(0)disk(0)fdisk(0)\\"

int dumptlb(argc, argv)
     int argc;
     char **argv;
{
  char temp[1024];
  int  i, wired;
  int  all = 0;
  FILE *fp;
  char name[80];
  unsigned long long entry_hi, entry_lo0, entry_lo1;
  unsigned pagemask;

  *name = '\0';
  
  argc--;
  argv++;
  if (argc)			/* args follow */
    {
      if (!strcmp(*argv,"all"))	/* dump all ? */
	{
	  argc--;
	  argv++;
	  all = 1;
	}
    }
  if (argc)
    {
      strcpy(name,TLBPATH);
      strcat(name,*argv);		/* must be a filename */
      argc--;
      argv++;
    }
  if (argc)
    {
      fprintf(stderr,"Illegal argument: %s\n\r", *argv);
      return 1;
    }
    
  if (*name)
    {
      fp = fopen(name, "w");
      if (fp < 0)
	{
	  printf("dumptlb: could not open `%s'.\r\n",name);
	  return;
	}
      printf("dumptlb: writing TLB dump to file `%s'...",name);
      fflush(stdout);
    }
  else
    fp = stdout;
  
  fprintf(fp, "\r\n\r\nTLB Dump:\r\n\r\n");
  wired = read_32bit_cp0_register(CP0_WIRED);
  __asm__ __volatile__("nop;nop;nop;nop");
  fprintf(fp, "Wired: %d\r\n\r\n", wired);
  
  for(i=0 ; i<48; i++)		/* print all TLB entries */
    {
      write_32bit_cp0_register(CP0_INDEX, i);
      __asm__ __volatile__("nop;nop;nop;nop"); /* DON'T REMOVE THESE NOPS !!! */
      __asm__ __volatile__("tlbr");
      __asm__ __volatile__("nop;nop;nop;nop"); /* R4000PC V2.2 NEEDS THEM !!! */	
      
      entry_hi = read_64bit_cp0_register(CP0_ENTRYHI);
      entry_lo0 = read_64bit_cp0_register(CP0_ENTRYLO0);
      entry_lo1 = read_64bit_cp0_register(CP0_ENTRYLO1);
      pagemask  = read_32bit_cp0_register(CP0_PAGEMASK);
      
      if ((entry_lo0 && VALID) || (entry_lo1 && VALID) || all)
	{
	  fprintf(fp, "TLB #%d:\r\n",i);
	  fprintf(fp, "\tEntryHi : %08x:%08x\t",
		  (unsigned int)(entry_hi >> 32), (unsigned int) entry_hi);
	  fprintf(fp, "(R=%1x VADR=%02x:%08x ASID=%04x)\r\n",
		  entry_hi >> 62,
		  entry_hi & 0xff00000000ULL,
		  entry_hi & 0xffffe000ULL,
		  entry_hi & 0xffULL);
	  fprintf(fp, "\tEntryLo0: %08x:%08x\t",
		  (unsigned int)(entry_lo0 >> 32), (unsigned int) entry_lo0);
	  fprintf(fp, "(PADR=%02x:%08x C=%d D=%d V=%d G=%d)\r\n",
		  (entry_lo0 & 0x3c000000) >> 25,
		  (entry_lo0 & 0x03ffffc0) << 6,
		  (entry_lo0 & 0x00000038) >> 3,
		  (entry_lo0 & 0x00000004) >> 2,
		  (entry_lo0 & 0x00000002) >> 1,
		  (entry_lo0 & 0x00000001));
	  fprintf(fp, "\tEntryLo1: %08x:%08x\t",
		  (unsigned int)(entry_lo1 >> 32), (unsigned int) entry_lo1);
	  fprintf(fp, "(PADR=%02x:%08x C=%d D=%d V=%d G=%d)\r\n",
		  (entry_lo1 & 0x3c000000) >> 25,
		  (entry_lo1 & 0x03ffffc0) << 6,
		  (entry_lo1 & 0x00000038) >> 3,
		  (entry_lo1 & 0x00000004) >> 2,
		  (entry_lo1 & 0x00000002) >> 1,
		  (entry_lo1 & 0x00000001));
	  fprintf(fp, "\tPageMask: %08x\r\n\r\n",pagemask);
	  if (!*name)
	    {
	      printf("Press a key for next\r\n");
	      getchar();
	    }
	}
    }

  if (*name)
    {
      fflush(fp);
      fclose(fp);
      printf(" done.\r\n");
    }
}

/* end of file commands.c */
