/* kdebug.c */
/* This implements a simple kernel debugger. */
/*
    Copyright (C) 1992  Ross Biro

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 

    The Author may be reached as bir7@leland.stanford.edu or
    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
*/

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/ptrace.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/string.h>

#define MAX_FIELDS 16

int i386dis(int pc, unsigned char *inbuf, char *outbuf);

static struct {
  char *name;
  int offset;
  int tssoff;
} regs[]=
{
#define SPNO 0
  {"esp",0},
#define DR0NO 1
  {"dr0",0,0},
  {"dr1",0,0},
  {"dr2",0,0},
  {"dr3",0,0},
  {"dr4",0,0},
  {"dr5",0,0},
  {"dr6",0,0},
  {"dr7",0,0},
#define DR7NO 8
#define DEBUGREG(x) (((x)>=DR0NO) && ((x)<= DR7NO))
#define DEBUGREGNO(x) ((x)-DR0NO)
  {"eax",-6,10},
  {"ebx",-12,13},
  {"ecx",-11,11},
  {"edx",-10,12},
  {"esi",-9,16},
  {"edi",-8,17},
  {"ebp",-7,15},
  {"ds",-5,21},
  {"es",-4,18},
  {"fs",-3,22},
  {"gs",-2,23},
  {"eip",0,8},
  {"cs",1,19},
  {"flags",2},
  {NULL,0,0}
};
#define WHITESPACE " \t\n\r"
#define HELP "-HELP"
#define USAGE "-USAGE"
#define is_printable(x) (((x) != '\r') && ((x)!='\n') && ((x) != 0x7f) && \
((x) != 0) )

static long last_err=-1;
static long *last_esp=0;

/* static unsigned char patch_buff[1024]={0,}; some unused memory for
					       patches. */
volatile int k_debug=0;

extern hard_reset_now(void);
extern void show_state(void);
extern show_task (int, struct task_struct *);
extern void show_mem(void);
static int atox(char *, unsigned long *);
extern volatile unsigned char kstatus;

static  unsigned char
get_byte(unsigned long addr)
{
  return (*(unsigned char *)addr);
}

static  unsigned long
get_long (unsigned long addr)
{
  return (*(unsigned long *) addr);
}

static  void
put_byte (unsigned long addr, unsigned char data)
{
  *(unsigned char *)addr = data;
}

static  void
put_long (unsigned long addr, unsigned long data)
{
  *(unsigned long *)addr = data;
}

static  void
sto_lower(char *s)
{
  int i;
  for (i=0; s[i] != 0; i++)
    {
      if (s[i] >= 'A' && s[i] <= 'Z') s[i]|=0x20;
    }
}

static  void
set_dr0(unsigned long value)
{
  __asm__("movl %0,%%db0"::"r" (value));
}

static  void
set_dr1(unsigned long value)
{
  __asm__("movl %0,%%db1"::"r" (value));
}

static  void
set_dr2(unsigned long value)
{
  __asm__("movl %0,%%db2"::"r" (value));
}

static  void
set_dr3(unsigned long value)
{
  __asm__("movl %0,%%db3"::"r" (value));
}

static  void
set_dr4(unsigned long value)
{
/*  __asm__("movl %0,%%db4"::"r" (value));*/
}

static  void
set_dr5(unsigned long value)
{
/*  __asm__("movl %0,%%db5"::"r" (value));*/
}

static  void
set_dr6(unsigned long value)
{
  __asm__("movl %0,%%db6"::"r" (value));
}

static  void
set_dr7(unsigned long value)
{
  __asm__("movl %0,%%db7"::"r" (value));
}

static  unsigned long
dr0(void)
{
  unsigned long res;
  __asm__("movl %%db0,%%eax":"=a" (res));
  return (res);
}

static  unsigned long
dr1(void)
{
  unsigned long res;
  __asm__("movl %%db1,%%eax":"=a" (res));
  return (res);
}

static  unsigned long
dr2(void)
{
  unsigned long res;
  __asm__("movl %%db2,%%eax":"=a" (res));
  return (res);
}

static  unsigned long
dr3(void)
{
  unsigned long res;
  __asm__("movl %%db3,%%eax":"=a" (res));
  return (res);
}

static  unsigned long
dr4(void)
{
  unsigned long res;
/*  __asm__("movl %%db4,%%eax":"=a" (res));*/
  res=0;
  return (res);
}

static  unsigned long
dr5(void)
{
  unsigned long res;
/*  __asm__("movl %%db5,%%eax":"=a" (res));*/
  res=0;
  return (res);
}

static  unsigned long
dr6(void)
{
  unsigned long res;
  __asm__("movl %%db6,%%eax":"=a" (res));
  return (res);
}

static  unsigned long
dr7(void)
{
  unsigned long res;
  __asm__("movl %%db7,%%eax":"=a" (res));
  return (res);
}

static last_disassem=0;
static void
disassem(long addr)
{
  /*these don't need to go on the stack. */
  static char obuff[80];
  static unsigned char ibuff[20];
  int i;
  int len;
  /* read in 20 bytes so we know we have more than enough. */
  for (i=0; i < 20; i++)
    {
      ibuff[i]=get_byte(addr+i);
    }
  len =i386dis(addr, ibuff, obuff);
  last_disassem = addr+len;
  printk ("%8.8X ",addr);
  for ( i = 0; i < 10; i++)
    {
      if (i < len)
	printk("%2.2X ",ibuff[i]);
      else
	printk("   ");
    }
  printk ("   %s\n", obuff);
}


static  void
printregs(void)
{
  printk("\n");
  printk("error code = %d\n",last_err);
  printk("CS:EIP - %4.4X:%8.8X ",last_esp[1]&0xffff,last_esp[0]);
  printk("EFLAGS - %8.8X\n",last_esp[2]);
  printk("EAX - %8.8X EBX - %8.8X ECX - %8.8X EDX - %8.8X\n",
	 last_esp[-6], last_esp[-12], last_esp[-11], last_esp[-10]);
  printk("ESI - %8.8X EDI - %8.8X EBP - %8.8X ESP - %8.8X\n",
	 last_esp[-9], last_esp[-8], last_esp[-7], last_esp+3);
  printk("ds - %4.4X es - %4.4X fs - %4.4X gs - %4.4X\n",
	 last_esp[-5]&0xffff, last_esp[-4]&0xffff,
	 last_esp[-3]&0xffff, last_esp[-2]&0xffff);
  printk("DR0 - %8.8X DR1 - %8.8X DR2 - %8.8X DR3 -%8.8X\n",
	 dr0(),dr1(),dr2(),dr3());
  printk("DR4 - %8.8X DR5 - %8.8X DR6 - %8.8X DR7 -%8.8X\n",
	 dr4(),dr5(),dr6(),dr7());
  printk("\n");
  disassem (last_esp[0]);

}

static  void
prompt(void)
{
  printk("\nk-debug> ");
}

void kdebug_init(void)
{
  set_dr7(2);
  set_dr6(0);
}

void start_debug(int key, struct pt_regs *pt)
{
  if (key & 0x80) return;
  if (k_debug)
    {
      kstatus ^= KEY_DBG;
      return;
    }
  kstatus |= KEY_DBG;
  k_debug++;
  last_err = pt->orig_eax;
  /* advance it to where we think the stack should be. */
  last_esp = (unsigned long *)pt + 12;
  prompt();
}

void enter_debug(long *sp, long errcode)
{
  last_err = errcode;
  last_esp=sp;
  kstatus |= KEY_DBG;
  k_debug=2;
  printregs();
  prompt();
  /* we can't let this task continue, but we can't safely switch out
     of it either.  So we just sit here in a busy wait loop. */
  sti(); /* make sure interrupts are enabled. */
  while (k_debug > 0);
}

static  int
get_regno(char *reg)
{
  int i;
  for (i=0; regs[i].name != NULL; i++)
    {
      if (strncmp(regs[i].name,reg,strlen(regs[i].name)) == 0)
	{
	  if (strlen(regs[i].name) != strlen(reg))
	    {
	      unsigned long val;
	      if (atox(reg+strlen(regs[i].name),&val))
		{
		  return (-1);
		}
	      return (i|(val+1)<< 8);
	    }
	  return (i);
	}
    }
  return (-1);
}

static  int
get_reg(int regno, unsigned long *value)
{
  if (DEBUGREG(regno))
    {
      switch (DEBUGREGNO(regno))
	{
	case 0:
	  *value = dr0();
	  break;
	case 1:
	  *value = dr1();
	  break;
	case 2:
	  *value = dr2();
	  break;
	case 3:
	  *value = dr3();
	  break;
	case 4:
	  *value = dr4();
	  break;
	case 5:
	  *value = dr5();
	  break;
	case 6:
	  *value = dr6();
	  break;
	case 7:
	  *value = dr7();
	  break;
	}
      return (0);
    }
  else
    {
      /* take care of eax0 etc. */
      if ((regno & 0xffffff00) != 0)
	{
	  int tmp;
	  tmp = regs[regno& 0xff].tssoff;
	  if (tmp == 0) return (-1);
	  *value=((long *)(&task[(regno>>8) -1]->tss))[tmp];
	  return (0);
	}
      if (regno == SPNO)
	{
	  *value =(long)(last_esp+3);
	  return (0);
	}
      *value = last_esp[regs[regno].offset];
      return (0);
    }
}

static  int
atoi(char *str, unsigned long *res)
{
  *res=0;
  while (*str != 0)
    {
      *res *= 10;
      if ((*str >= '0') && (*str <= '9'))
	*res += *str - '0';
      else
	return (-1);
      str++;
    }
  return (0);

}

static int
atox(char *str, unsigned long *res)
{
  int reg_no;

  /* see if we need to use a reg. */
  if (*str == '%')
    {
      reg_no=get_regno(str+1);
      if (reg_no < 0) return (reg_no);
      return (get_reg(reg_no,res));
    }

  /* see if it's indirect. */
  if (*str == '@')
    {
      if (atox(str+1,res) < 0)
	return (-1);
      *res = get_long(*res);
      return(0);
    }

  /* see if it's a decimal number */
  if (*str == '#' )
    {
      return (atoi(str+1,res));
    }

  *res=0;
  while (*str != 0)
    {
      *res= *res<<4;
      if ((*str >= '0') && (*str <= '9'))
	*res += *str - '0';
      else
	if ((*str >= 'a') && (*str <= 'f'))
	  *res += *str -'a' +10;
      else
	return (-1);
      str++;
    }
  return (0);

}

static  int
set_reg(int regno, unsigned long value)
{
  if (DEBUGREG(regno))
    {
      switch (DEBUGREGNO(regno))
	{
	case 0:
	  set_dr0(value);
	  break;
	case 1:
	  set_dr1(value);
	  break;
	case 2:
	  set_dr2(value);
	  break;
	case 3:
	  set_dr3(value);
	  break;
	case 4:
	  set_dr4(value);
	  break;
	case 5:
	  set_dr5(value);
	  break;
	case 6:
	  set_dr6(value);
	  break;
	case 7:
	  set_dr7(value);
	  break;
	}
      return (0);
    }
  else
    {
      /* take care of eax0 etc. */
      if ((regno & 0xffffff00) != 0)
	{
	  int tmp;
	  tmp = regs[regno&0xff].tssoff;
	  if (tmp == 0) return (-1);
  	  ((long *)(&task[(regno>>8) -1]->tss))[tmp]=value;
	  return (0);
	}
      if (regno == SPNO)
	return (-1);
      last_esp[regs[regno].offset]=value;
      return (0);
    }
}

static  void
print_reg(int regno)
{
  unsigned long value;
  if (get_reg(regno,&value))
    {
      printk ("Bad Register\n");
      return;
    }
  if ((regno & 0xffffff00) != 0)
    {
      printk ("%s%x - %8.8X\n",regs[regno&0xff].name, (regno>>8)-1,value);
    }
  else
    {
      printk ("%s - %8.8X\n",regs[regno&0xff].name,value);
    }
}

typedef void (*func_ptr)(int, char **);

struct dcmd
{
  char *name;
  func_ptr func;
};

static void
dbg_run(int argc, char *argv[])
{
  if (argc > 1)
    {
      if (strcmp (argv[1],HELP) == 0)
	{
	  printk("Run -- exits the kernel debugger,"
		 "and resumes normal execution\n");
	}
      else
	{
	  printk("run\n");
	}
      return;
    }
  printk ("Running\n");
  k_debug=0;
  kstatus &= ~KEY_DBG;
}

static  void
dump_line (long address)
{
  unsigned long addr;
  int i;
  unsigned char byte;

  addr=address&~0xf;
  printk ("%8.8X: ",addr);
  for (i=0; i < 16; i++)
    {
      if (addr+i >= address)
	{
	  printk ("%2.2X",get_byte(addr+i));
	  if (i==7)
	    printk ("-");
	  else
	    printk (" ");
	}
      else
	{
	  printk ("   ");
	}
    }
  printk (" ");
  for (i=0; i < 16; i++)
    {
      if (addr+i >= address)
	{
	  byte=get_byte(addr+i);
	  if (is_printable(byte))
	    printk ("%c",get_byte(addr+i));
	  else
	    printk (".");
	}
    }
  printk ("\n");
}

static void
dbg_dump(int argc, char *argv[])
{
  static long last_dump_addr=0;
  unsigned long value;
  long len;

  if (argc == 1)
    {
      dump_line(last_dump_addr);
      last_dump_addr+=16;
      return;
    }
  if (strcmp(argv[1],HELP) == 0)
    {
      printk("dump -- display bytes of memory.\n");
      return;
    }
  if ( argc > 3 || atox(argv[1],&value) )
    {
      printk("dump [address [len]]\n");
      return;
    }
  if (argc == 3)
    {
      if (atox(argv[2], (unsigned long *)&len))
	{
	  printk ("bad Value\n");
	  return;
	}
    }
  else
    {
      len = ((value+16)&~0xf) - value;
    }

  while (len > 0)
    {
      
      dump_line(value);
      len-=16-(value&0xf);
      value+=16-(value&0xf);
    }
  last_dump_addr = value;
}

static void
dbg_rboot(int argc, char *argv[])
{
  if (argc > 1)
    {
      if (strcmp (argv[1],HELP) == 0)
	{
	  printk("Reboot -- Resets the computer (Hopefully causing"
		"a reboot.)\n");
	}
      else
	{
	  printk("reboot\n");
	}
      return;
    }
  printk ("Rebooting\n");
  hard_reset_now();
}

/* display the registers, a single register, or change the value of
   a single register. */

static void
dbg_regs(int argc, char *argv[])
{
  int regno;
  unsigned long value;
  if (argc == 1)
    {
      printregs();
      return;
    }
  if (strcmp(argv[1],HELP) == 0)
    {
      printk("regs -- display the registers, a signle register, or changed\n"
	     "        the value of a single register.\n");
	return;
    }
  if ((regno=get_regno(argv[1])) < 0 || argc > 3)
    {
      printk("regs [reg [value]]\n");
      return;
    }

  if (argc == 2)
    {
      print_reg(regno);
      return;
    }

  if (atox(argv[2],&value))
    {
      printk ("Bad Hex number %s\n",argv[2]);
      return;
    }
  set_reg(regno, value);
  print_reg(regno);
}

static void
dbg_mem (int argc, char *argv[])
{
  if (argc > 1)
    {
      if (strcmp (argv[1],HELP)==0)
	{
	  printk("mem -- print memory status\n");
	  return;
	}
      printk("mem\n");
      return;
    }
  show_mem();
}


static void
dbg_task (int argc, char *argv[])
{
  unsigned long value;
  if (argc > 1)
    {
      if (strcmp (argv[1],HELP)==0)
	{
	  printk("task -- the task struct for one or all tasks.\n");
	  return;
	}
      if (atox(argv[1],&value))
	{
	  printk("task [n]\n");
	  return;
	}

      if (task[value] != NULL)
	show_task(value, task[value]);
      else
	printk("task[%X]=NULL\n",value);

      return;
    }
  printk ("\n");
  show_state();
}

static void
dbg_stack (int argc, char *argv[])
{
  unsigned long len;
  long *addr;
  if (argc > 1)
    {
      if (strcmp (argv[1],HELP)==0)
	{
	  printk("stack -- display the contents of the stack.\n");
	  return;
	}

      if (atox(argv[1],&len))
	{
	  printk("stack [len]\n");
	  return;
	}
    }
  else
    {
      len = 10;
    }
  addr = (long *)(last_esp+3);
  while (len > 0)
    {
      printk ("%8.8X\n",addr[len]);
      len --;
    }
}

static void
dbg_nocmd(int argc, char *argv[])
{
  printk("Unrecognized Command - %s\n", argv[0]);
}


static void
dbg_current(int argc, char *argv[])
{
  int i;
  if (argc > 1)
    {
      if (strcmp (argv[1],HELP) == 0)
	{
	  printk("Current -- prints the task struct assosiated\n"
		 "           to the current task\n");
	}
      else
	{
	  printk("current\n");
	}
      return;
    }
  for (i=0; i <NR_TASKS; i++)
    {
      if (task[i]==current)
	{
	  show_task(i,current);
	  return;
	}
    }
  printk ("Unable to locate current task????\n");
}

static void
dbg_list(int argc, char *argv[])
{
  unsigned long value;
  unsigned long len;

  if (argc == 1)
    {
      len=20;
    }
  else
    {
      if (strcmp(argv[1],HELP) == 0)
	{
	  printk("list -- disassemble instructions.\n");
	  return;
	}
      if ( argc > 3 || atox(argv[1],&value) )
	{
	  printk("list [address [len]]\n");
	  return;
	}
      if (argc == 3)
	{
	  if (atox(argv[2], &len))
	    {
	      printk ("bad Value\n");
	      return;
	    }
	}
      else
	{
	  len = 20;
	}
      last_disassem=value;
    }

  while (len > 0)
    {
      disassem(last_disassem);
      len --;
    }
}

static void
dbg_break(int argc, char *argv[])
{
  unsigned long value;
  unsigned long len;
  short type;
  unsigned long num;

  if (argc == 1)
    {
      /*list break points. */
      for (num =0; num < 4; num++)
	{
	  if (dr6() & (0x1<< num))
	    printk ("O ");
	  else
	    printk ("  ");
	  
	  printk ("break pt %1.1d - ",num);
	  value = dr7();
	  if (value & (0x2<< (2*num)))
	    {
	      get_reg(DR0NO+num,&len);
	      printk ("%8.8X ",len);
	      if ((value >> (16+4*num)) & 0x3)
		printk ("Data len %d\n", ((value >> (18+4*num)) & 3) + 1);
	      else
		printk ("Code len %d\n", ((value >> (18+4*num)) & 3) + 1);
	    }
	  else
	    printk ("Not Set\n");
	}
      return;
    }

  if (strcmp(argv[1],HELP) == 0)
    {
      printk("break -- set or list a data/code breakpoint.\n");
      return;
    }

  if (argc == 2 && atox(argv[2],&value))
    {
      /* turn off break value. */
      set_dr7(dr7()&~(1<<value));
      return;
    }
    
  if ( (argc != 4 && argc != 5) ||
      atox(argv[2],&value) || strlen(argv[3]) != 1
      || atox(argv[1],&num) ||  num > 3)
    {
      printk("break [num address type [len]]\n");
      return;
    }

  if (*argv[3] == 'c')
    type = 0;
  else if (*argv[3] == 'w')
    type = 1;
  else
    type = 3;

  if (argc == 5)
    {
      if (atox(argv[4], &len) || len > 4 || len == 3 || len == 0)
	{
	  printk ("bad len value\n");
	  return;
	}
    }
  else
    {
      len = 4;
    }

  if (type == 0) len = 1; /* only valid len for code brk pts is 1 */
  len--;
  set_reg(DR0NO+num,value);
  set_reg(DR0NO+7,dr7()|(0x2 << (num*2)) | (((len<<2) | type) << (16+4*num)));
}

static void
dbg_edit (int argc, char *argv[])
{
  int i;
  unsigned long addr;
  unsigned long data;

  if (argc > 1 && strcmp (argv[1],HELP) == 0)
    {
      printk ("edit -- change some bytes in memory. \n");
      return;
    }

  if (atox(argv[1],&addr) || argc < 3) 
    {
      printk ("edit addr byte0 [byte1 ...]\n");
      return;
    }

  for (i = 2; i < argc; i++)
    {
      if (atox(argv[i],&data) || data > 255)
	{
	  printk ("bad value - %s\n",argv[i]);
	  return;
	}
      put_byte (addr, data);
      addr++;
    }
}


static void
dbg_outb (int argc, char *argv[])
{
  int i;
  unsigned long port;
  unsigned long data;

  if (argc > 1 && strcmp (argv[1],HELP) == 0)
    {
      printk ("outb -- Output a byte to an i/o port.\n");
      return;
    }

  if (atox(argv[1],&port) || argc < 3) 
    {
      printk ("outb port byte0 [byte1 ...]\n");
      return;
    }

  for (i = 2; i < argc; i++)
    {
      if (atox(argv[i],&data) || data > 255)
	{
	  printk ("bad value - %s\n",argv[i]);
	  return;
	}
      outb_p (data, port);
    }
}


static void
dbg_inb (int argc, char *argv[])
{
  unsigned long port;

  if (argc > 1 && strcmp (argv[1],HELP) == 0)
    {
      printk ("inb -- Input a byte from an i/o port.\n");
      return;
    }

  if (atox(argv[1],&port) || argc != 2) 
    {
      printk ("inb port\n");
      return;
    }

  printk ("in (%X) = %2.2X\n",port,inb(port));
}

static void  dbg_help(int, char **);

static struct dcmd dcmds[]=
{
  {"help", dbg_help},
  {"regs",dbg_regs},
  {"run",dbg_run},
  {"reboot",dbg_rboot},
  {"tasks", dbg_task},
  {"mem", dbg_mem},
  {"dump", dbg_dump},
  {"list", dbg_list},
  {"break", dbg_break},
  {"edit", dbg_edit},
  {"stack", dbg_stack},
  {"current", dbg_current},
  {"outb", dbg_outb},
  {"inb", dbg_inb},
  {NULL, dbg_nocmd}
};

static void
dbg_help(int argc, char *argv[])
{
  int i;
  static char *usage[]={
    "",
    USAGE
    };
  static char *help[]={
    "",
    HELP
    };

  if (argc == 1)
    {
      for (i = 0; dcmds[i].name != NULL; i++)
	{
	  dcmds[i].func(2, usage);
	}
      return;
    }

  if (strcmp (argv[1],HELP) == 0)
    {
      printk ("help -- Print a list of commands. \n");
      return;
    }
  if (strcmp (argv[1],USAGE) == 0)
    {
      printk ("help [cmd]\n");
      return;
    }
  for (i = 0; dcmds[i].name != NULL; i++)
    {
      if (strncmp (argv[1],dcmds[i].name,strlen(argv[1])) == 0)
	{
	  dcmds[i].func(2,help);
	}
    }
}

static  int
split_line(char *fields[], char **string)
{
  int c;
  char *str;
  str = *string;
  for (c=0; c < MAX_FIELDS; c++)
    {
      fields[c]=NULL;
      str += strspn(str,WHITESPACE);
      if (*str == ';')
	{
	  *string = str+1;
	  return (c);
	}
      if (*str == 0)
	{
	  *string=str;
	  return (c);
	}
      fields[c]=str;
      str = strpbrk(str,WHITESPACE);
      if (str == NULL || *str == 0) 
	{
	  fields[c+1] = NULL;
	  if (str != NULL)
	    {
	      *string=str;
	    }
	  else
	    {
	      *string=*string+strlen(*string);
	    }
	  return (c+1);
	}
      *str=0;
      str++;
    }
  *string=str;
  return (c);
}

void
do_dcmd(char *cmd)
{
  int i;
  int count;
  static char *fields[MAX_FIELDS];
  printk("\n");
  sto_lower(cmd);
  
  while (*cmd!=0)
    {
      count=split_line(fields,&cmd);
      if (count > 0)
	{
	  for (i=0; dcmds[i].name != NULL; i++)
	    {
	      if (strncmp(dcmds[i].name,fields[0],strlen(fields[0])) == 0)
		break;
	    }
	  dcmds[i].func(count, fields);
	}
    }
}

void debug_char (unsigned char c, struct pt_regs *pt)
{
  static char buff[80];
  static bptr=0;
  /* this routine gets called with every keystroke when
     the debugger is active.  We need to echo the character
     and store it.  Unless it's a return in which case we
     act on it. */

  if (!k_debug)
    {
      kstatus &= ~KEY_DBG;
      return;
    }
  if (k_debug == 1)
    {
      last_esp = (unsigned long *)pt + 12;
      last_err = pt->orig_eax;
    }

  if (c == 0x7f)
    {
      if (bptr > 0)
	{
	  bptr --;
	  printk("%c",c);
	}
    }
  else
    {
      buff[bptr++]=c;
      printk("%c",c);
      
      if (bptr == 79 || c==13)
	{
	  buff[bptr]=0;
	  bptr=0;
	  do_dcmd(buff);
	  if (k_debug != 0) prompt();
	}
    }
}


