1780 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1780 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
| /****************************************************************************
 | |
| 
 | |
| 		THIS SOFTWARE IS NOT COPYRIGHTED
 | |
| 
 | |
|    HP offers the following for use in the public domain.  HP makes no
 | |
|    warranty with regard to the software or it's performance and the
 | |
|    user accepts the software "AS IS" with all faults.
 | |
| 
 | |
|    HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
 | |
|    TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 | |
|    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 | |
| 
 | |
| ****************************************************************************/
 | |
| 
 | |
| /****************************************************************************
 | |
|  *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
 | |
|  *
 | |
|  *  Module name: remcom.c $
 | |
|  *  Revision: 1.34 $
 | |
|  *  Date: 91/03/09 12:29:49 $
 | |
|  *  Contributor:     Lake Stevens Instrument Division$
 | |
|  *
 | |
|  *  Description:     low level support for gdb debugger. $
 | |
|  *
 | |
|  *  Considerations:  only works on target hardware $
 | |
|  *
 | |
|  *  Written by:      Glenn Engel $
 | |
|  *  ModuleState:     Experimental $
 | |
|  *
 | |
|  *  NOTES:           See Below $
 | |
|  *
 | |
|  *  Modified for M32R by Michael Snyder, Cygnus Support.
 | |
|  *
 | |
|  *  To enable debugger support, two things need to happen.  One, a
 | |
|  *  call to set_debug_traps() is necessary in order to allow any breakpoints
 | |
|  *  or error conditions to be properly intercepted and reported to gdb.
 | |
|  *  Two, a breakpoint needs to be generated to begin communication.  This
 | |
|  *  is most easily accomplished by a call to breakpoint().  Breakpoint()
 | |
|  *  simulates a breakpoint by executing a trap #1.
 | |
|  *
 | |
|  *  The external function exceptionHandler() is
 | |
|  *  used to attach a specific handler to a specific M32R vector number.
 | |
|  *  It should use the same privilege level it runs at.  It should
 | |
|  *  install it as an interrupt gate so that interrupts are masked
 | |
|  *  while the handler runs.
 | |
|  *
 | |
|  *  Because gdb will sometimes write to the stack area to execute function
 | |
|  *  calls, this program cannot rely on using the supervisor stack so it
 | |
|  *  uses it's own stack area reserved in the int array remcomStack.
 | |
|  *
 | |
|  *************
 | |
|  *
 | |
|  *    The following gdb commands are supported:
 | |
|  *
 | |
|  * command          function                               Return value
 | |
|  *
 | |
|  *    g             return the value of the CPU registers  hex data or ENN
 | |
|  *    G             set the value of the CPU registers     OK or ENN
 | |
|  *
 | |
|  *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
 | |
|  *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
 | |
|  *    XAA..AA,LLLL: Write LLLL binary bytes at address     OK or ENN
 | |
|  *                  AA..AA
 | |
|  *
 | |
|  *    c             Resume at current address              SNN   ( signal NN)
 | |
|  *    cAA..AA       Continue at address AA..AA             SNN
 | |
|  *
 | |
|  *    s             Step one instruction                   SNN
 | |
|  *    sAA..AA       Step one instruction from AA..AA       SNN
 | |
|  *
 | |
|  *    k             kill
 | |
|  *
 | |
|  *    ?             What was the last sigval ?             SNN   (signal NN)
 | |
|  *
 | |
|  * All commands and responses are sent with a packet which includes a
 | |
|  * checksum.  A packet consists of
 | |
|  *
 | |
|  * $<packet info>#<checksum>.
 | |
|  *
 | |
|  * where
 | |
|  * <packet info> :: <characters representing the command or response>
 | |
|  * <checksum>    :: <two hex digits computed as modulo 256 sum of <packetinfo>>
 | |
|  *
 | |
|  * When a packet is received, it is first acknowledged with either '+' or '-'.
 | |
|  * '+' indicates a successful transfer.  '-' indicates a failed transfer.
 | |
|  *
 | |
|  * Example:
 | |
|  *
 | |
|  * Host:                  Reply:
 | |
|  * $m0,10#2a               +$00010203040506070809101112131415#42
 | |
|  *
 | |
|  ****************************************************************************/
 | |
| 
 | |
| 
 | |
| /************************************************************************
 | |
|  *
 | |
|  * external low-level support routines
 | |
|  */
 | |
| extern void putDebugChar ();	/* write a single character      */
 | |
| extern int getDebugChar ();	/* read and return a single char */
 | |
| extern void exceptionHandler ();	/* assign an exception handler   */
 | |
| 
 | |
| /*****************************************************************************
 | |
|  * BUFMAX defines the maximum number of characters in inbound/outbound buffers
 | |
|  * at least NUMREGBYTES*2 are needed for register packets 
 | |
|  */
 | |
| #define BUFMAX 400
 | |
| 
 | |
| static char initialized;	/* boolean flag. != 0 means we've been initialized */
 | |
| 
 | |
| int remote_debug;
 | |
| /*  debug >  0 prints ill-formed commands in valid packets & checksum errors */
 | |
| 
 | |
| static const unsigned char hexchars[] = "0123456789abcdef";
 | |
| 
 | |
| #define NUMREGS 24
 | |
| 
 | |
| /* Number of bytes of registers.  */
 | |
| #define NUMREGBYTES (NUMREGS * 4)
 | |
| enum regnames
 | |
| { R0, R1, R2, R3, R4, R5, R6, R7,
 | |
|   R8, R9, R10, R11, R12, R13, R14, R15,
 | |
|   PSW, CBR, SPI, SPU, BPC, PC, ACCL, ACCH
 | |
| };
 | |
| 
 | |
| enum SYS_calls
 | |
| {
 | |
|   SYS_null,
 | |
|   SYS_exit,
 | |
|   SYS_open,
 | |
|   SYS_close,
 | |
|   SYS_read,
 | |
|   SYS_write,
 | |
|   SYS_lseek,
 | |
|   SYS_unlink,
 | |
|   SYS_getpid,
 | |
|   SYS_kill,
 | |
|   SYS_fstat,
 | |
|   SYS_sbrk,
 | |
|   SYS_fork,
 | |
|   SYS_execve,
 | |
|   SYS_wait4,
 | |
|   SYS_link,
 | |
|   SYS_chdir,
 | |
|   SYS_stat,
 | |
|   SYS_utime,
 | |
|   SYS_chown,
 | |
|   SYS_chmod,
 | |
|   SYS_time,
 | |
|   SYS_pipe
 | |
| };
 | |
| 
 | |
| static int registers[NUMREGS];
 | |
| 
 | |
| #define STACKSIZE 8096
 | |
| static unsigned char remcomInBuffer[BUFMAX];
 | |
| static unsigned char remcomOutBuffer[BUFMAX];
 | |
| static int remcomStack[STACKSIZE / sizeof (int)];
 | |
| static int *stackPtr = &remcomStack[STACKSIZE / sizeof (int) - 1];
 | |
| 
 | |
| static unsigned int save_vectors[18];	/* previous exception vectors */
 | |
| 
 | |
| /* Indicate to caller of mem2hex or hex2mem that there has been an error. */
 | |
| static volatile int mem_err = 0;
 | |
| 
 | |
| /* Store the vector number here (since GDB only gets the signal
 | |
|    number through the usual means, and that's not very specific).  */
 | |
| int gdb_m32r_vector = -1;
 | |
| 
 | |
| #if 0
 | |
| #include "syscall.h"		/* for SYS_exit, SYS_write etc. */
 | |
| #endif
 | |
| 
 | |
| /* Global entry points:
 | |
|  */
 | |
| 
 | |
| extern void handle_exception (int);
 | |
| extern void set_debug_traps (void);
 | |
| extern void breakpoint (void);
 | |
| 
 | |
| /* Local functions:
 | |
|  */
 | |
| 
 | |
| static int computeSignal (int);
 | |
| static void putpacket (unsigned char *);
 | |
| static unsigned char *getpacket (void);
 | |
| 
 | |
| static unsigned char *mem2hex (unsigned char *, unsigned char *, int, int);
 | |
| static unsigned char *hex2mem (unsigned char *, unsigned char *, int, int);
 | |
| static int hexToInt (unsigned char **, int *);
 | |
| static unsigned char *bin2mem (unsigned char *, unsigned char *, int, int);
 | |
| static void stash_registers (void);
 | |
| static void restore_registers (void);
 | |
| static int prepare_to_step (int);
 | |
| static int finish_from_step (void);
 | |
| static unsigned long crc32 (unsigned char *, int, unsigned long);
 | |
| 
 | |
| static void gdb_error (char *, char *);
 | |
| static int gdb_putchar (int), gdb_puts (char *), gdb_write (char *, int);
 | |
| 
 | |
| static unsigned char *strcpy (unsigned char *, const unsigned char *);
 | |
| static int strlen (const unsigned char *);
 | |
| 
 | |
| /*
 | |
|  * This function does all command procesing for interfacing to gdb.
 | |
|  */
 | |
| 
 | |
| void
 | |
| handle_exception (int exceptionVector)
 | |
| {
 | |
|   int sigval, stepping;
 | |
|   int addr, length, i;
 | |
|   unsigned char *ptr;
 | |
|   unsigned char buf[16];
 | |
|   int binary;
 | |
| 
 | |
|   if (!finish_from_step ())
 | |
|     return;			/* "false step": let the target continue */
 | |
| 
 | |
|   gdb_m32r_vector = exceptionVector;
 | |
| 
 | |
|   if (remote_debug)
 | |
|     {
 | |
|       mem2hex ((unsigned char *) &exceptionVector, buf, 4, 0);
 | |
|       gdb_error ("Handle exception %s, ", buf);
 | |
|       mem2hex ((unsigned char *) ®isters[PC], buf, 4, 0);
 | |
|       gdb_error ("PC == 0x%s\n", buf);
 | |
|     }
 | |
| 
 | |
|   /* reply to host that an exception has occurred */
 | |
|   sigval = computeSignal (exceptionVector);
 | |
| 
 | |
|   ptr = remcomOutBuffer;
 | |
| 
 | |
|   *ptr++ = 'T';			/* notify gdb with signo, PC, FP and SP */
 | |
|   *ptr++ = hexchars[sigval >> 4];
 | |
|   *ptr++ = hexchars[sigval & 0xf];
 | |
| 
 | |
|   *ptr++ = hexchars[PC >> 4];
 | |
|   *ptr++ = hexchars[PC & 0xf];
 | |
|   *ptr++ = ':';
 | |
|   ptr = mem2hex ((unsigned char *) ®isters[PC], ptr, 4, 0);	/* PC */
 | |
|   *ptr++ = ';';
 | |
| 
 | |
|   *ptr++ = hexchars[R13 >> 4];
 | |
|   *ptr++ = hexchars[R13 & 0xf];
 | |
|   *ptr++ = ':';
 | |
|   ptr = mem2hex ((unsigned char *) ®isters[R13], ptr, 4, 0);	/* FP */
 | |
|   *ptr++ = ';';
 | |
| 
 | |
|   *ptr++ = hexchars[R15 >> 4];
 | |
|   *ptr++ = hexchars[R15 & 0xf];
 | |
|   *ptr++ = ':';
 | |
|   ptr = mem2hex ((unsigned char *) ®isters[R15], ptr, 4, 0);	/* SP */
 | |
|   *ptr++ = ';';
 | |
|   *ptr++ = 0;
 | |
| 
 | |
|   if (exceptionVector == 0)	/* simulated SYS call stuff */
 | |
|     {
 | |
|       mem2hex ((unsigned char *) ®isters[PC], buf, 4, 0);
 | |
|       switch (registers[R0])
 | |
| 	{
 | |
| 	case SYS_exit:
 | |
| 	  gdb_error ("Target program has exited at %s\n", buf);
 | |
| 	  ptr = remcomOutBuffer;
 | |
| 	  *ptr++ = 'W';
 | |
| 	  sigval = registers[R1] & 0xff;
 | |
| 	  *ptr++ = hexchars[sigval >> 4];
 | |
| 	  *ptr++ = hexchars[sigval & 0xf];
 | |
| 	  *ptr++ = 0;
 | |
| 	  break;
 | |
| 	case SYS_open:
 | |
| 	  gdb_error ("Target attempts SYS_open call at %s\n", buf);
 | |
| 	  break;
 | |
| 	case SYS_close:
 | |
| 	  gdb_error ("Target attempts SYS_close call at %s\n", buf);
 | |
| 	  break;
 | |
| 	case SYS_read:
 | |
| 	  gdb_error ("Target attempts SYS_read call at %s\n", buf);
 | |
| 	  break;
 | |
| 	case SYS_write:
 | |
| 	  if (registers[R1] == 1 ||	/* write to stdout  */
 | |
| 	      registers[R1] == 2)	/* write to stderr  */
 | |
| 	    {			/* (we can do that) */
 | |
| 	      registers[R0] =
 | |
| 		gdb_write ((void *) registers[R2], registers[R3]);
 | |
| 	      return;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    gdb_error ("Target attempts SYS_write call at %s\n", buf);
 | |
| 	  break;
 | |
| 	case SYS_lseek:
 | |
| 	  gdb_error ("Target attempts SYS_lseek call at %s\n", buf);
 | |
| 	  break;
 | |
| 	case SYS_unlink:
 | |
| 	  gdb_error ("Target attempts SYS_unlink call at %s\n", buf);
 | |
| 	  break;
 | |
| 	case SYS_getpid:
 | |
| 	  gdb_error ("Target attempts SYS_getpid call at %s\n", buf);
 | |
| 	  break;
 | |
| 	case SYS_kill:
 | |
| 	  gdb_error ("Target attempts SYS_kill call at %s\n", buf);
 | |
| 	  break;
 | |
| 	case SYS_fstat:
 | |
| 	  gdb_error ("Target attempts SYS_fstat call at %s\n", buf);
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  gdb_error ("Target attempts unknown SYS call at %s\n", buf);
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   putpacket (remcomOutBuffer);
 | |
| 
 | |
|   stepping = 0;
 | |
| 
 | |
|   while (1 == 1)
 | |
|     {
 | |
|       remcomOutBuffer[0] = 0;
 | |
|       ptr = getpacket ();
 | |
|       binary = 0;
 | |
|       switch (*ptr++)
 | |
| 	{
 | |
| 	default:		/* Unknown code.  Return an empty reply message. */
 | |
| 	  break;
 | |
| 	case 'R':
 | |
| 	  if (hexToInt (&ptr, &addr))
 | |
| 	    registers[PC] = addr;
 | |
| 	  strcpy (remcomOutBuffer, "OK");
 | |
| 	  break;
 | |
| 	case '!':
 | |
| 	  strcpy (remcomOutBuffer, "OK");
 | |
| 	  break;
 | |
| 	case 'X':		/* XAA..AA,LLLL:<binary data>#cs */
 | |
| 	  binary = 1;
 | |
| 	case 'M':		/* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
 | |
| 	  /* TRY TO READ '%x,%x:'.  IF SUCCEED, SET PTR = 0 */
 | |
| 	  {
 | |
| 	    if (hexToInt (&ptr, &addr))
 | |
| 	      if (*(ptr++) == ',')
 | |
| 		if (hexToInt (&ptr, &length))
 | |
| 		  if (*(ptr++) == ':')
 | |
| 		    {
 | |
| 		      mem_err = 0;
 | |
| 		      if (binary)
 | |
| 			bin2mem (ptr, (unsigned char *) addr, length, 1);
 | |
| 		      else
 | |
| 			hex2mem (ptr, (unsigned char *) addr, length, 1);
 | |
| 		      if (mem_err)
 | |
| 			{
 | |
| 			  strcpy (remcomOutBuffer, "E03");
 | |
| 			  gdb_error ("memory fault", "");
 | |
| 			}
 | |
| 		      else
 | |
| 			{
 | |
| 			  strcpy (remcomOutBuffer, "OK");
 | |
| 			}
 | |
| 		      ptr = 0;
 | |
| 		    }
 | |
| 	    if (ptr)
 | |
| 	      {
 | |
| 		strcpy (remcomOutBuffer, "E02");
 | |
| 	      }
 | |
| 	  }
 | |
| 	  break;
 | |
| 	case 'm':		/* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
 | |
| 	  /* TRY TO READ %x,%x.  IF SUCCEED, SET PTR = 0 */
 | |
| 	  if (hexToInt (&ptr, &addr))
 | |
| 	    if (*(ptr++) == ',')
 | |
| 	      if (hexToInt (&ptr, &length))
 | |
| 		{
 | |
| 		  ptr = 0;
 | |
| 		  mem_err = 0;
 | |
| 		  mem2hex ((unsigned char *) addr, remcomOutBuffer, length,
 | |
| 			   1);
 | |
| 		  if (mem_err)
 | |
| 		    {
 | |
| 		      strcpy (remcomOutBuffer, "E03");
 | |
| 		      gdb_error ("memory fault", "");
 | |
| 		    }
 | |
| 		}
 | |
| 	  if (ptr)
 | |
| 	    {
 | |
| 	      strcpy (remcomOutBuffer, "E01");
 | |
| 	    }
 | |
| 	  break;
 | |
| 	case '?':
 | |
| 	  remcomOutBuffer[0] = 'S';
 | |
| 	  remcomOutBuffer[1] = hexchars[sigval >> 4];
 | |
| 	  remcomOutBuffer[2] = hexchars[sigval % 16];
 | |
| 	  remcomOutBuffer[3] = 0;
 | |
| 	  break;
 | |
| 	case 'd':
 | |
| 	  remote_debug = !(remote_debug);	/* toggle debug flag */
 | |
| 	  break;
 | |
| 	case 'g':		/* return the value of the CPU registers */
 | |
| 	  mem2hex ((unsigned char *) registers, remcomOutBuffer, NUMREGBYTES,
 | |
| 		   0);
 | |
| 	  break;
 | |
| 	case 'P':		/* set the value of a single CPU register - return OK */
 | |
| 	  {
 | |
| 	    int regno;
 | |
| 
 | |
| 	    if (hexToInt (&ptr, ®no) && *ptr++ == '=')
 | |
| 	      if (regno >= 0 && regno < NUMREGS)
 | |
| 		{
 | |
| 		  int stackmode;
 | |
| 
 | |
| 		  hex2mem (ptr, (unsigned char *) ®isters[regno], 4, 0);
 | |
| 		  /*
 | |
| 		   * Since we just changed a single CPU register, let's
 | |
| 		   * make sure to keep the several stack pointers consistant.
 | |
| 		   */
 | |
| 		  stackmode = registers[PSW] & 0x80;
 | |
| 		  if (regno == R15)	/* stack pointer changed */
 | |
| 		    {		/* need to change SPI or SPU */
 | |
| 		      if (stackmode == 0)
 | |
| 			registers[SPI] = registers[R15];
 | |
| 		      else
 | |
| 			registers[SPU] = registers[R15];
 | |
| 		    }
 | |
| 		  else if (regno == SPU)	/* "user" stack pointer changed */
 | |
| 		    {
 | |
| 		      if (stackmode != 0)	/* stack in user mode: copy SP */
 | |
| 			registers[R15] = registers[SPU];
 | |
| 		    }
 | |
| 		  else if (regno == SPI)	/* "interrupt" stack pointer changed */
 | |
| 		    {
 | |
| 		      if (stackmode == 0)	/* stack in interrupt mode: copy SP */
 | |
| 			registers[R15] = registers[SPI];
 | |
| 		    }
 | |
| 		  else if (regno == PSW)	/* stack mode may have changed! */
 | |
| 		    {		/* force SP to either SPU or SPI */
 | |
| 		      if (stackmode == 0)	/* stack in user mode */
 | |
| 			registers[R15] = registers[SPI];
 | |
| 		      else	/* stack in interrupt mode */
 | |
| 			registers[R15] = registers[SPU];
 | |
| 		    }
 | |
| 		  strcpy (remcomOutBuffer, "OK");
 | |
| 		  break;
 | |
| 		}
 | |
| 	    strcpy (remcomOutBuffer, "E01");
 | |
| 	    break;
 | |
| 	  }
 | |
| 	case 'G':		/* set the value of the CPU registers - return OK */
 | |
| 	  hex2mem (ptr, (unsigned char *) registers, NUMREGBYTES, 0);
 | |
| 	  strcpy (remcomOutBuffer, "OK");
 | |
| 	  break;
 | |
| 	case 's':		/* sAA..AA      Step one instruction from AA..AA(optional) */
 | |
| 	  stepping = 1;
 | |
| 	case 'c':		/* cAA..AA      Continue from address AA..AA(optional) */
 | |
| 	  /* try to read optional parameter, pc unchanged if no parm */
 | |
| 	  if (hexToInt (&ptr, &addr))
 | |
| 	    registers[PC] = addr;
 | |
| 
 | |
| 	  if (stepping)		/* single-stepping */
 | |
| 	    {
 | |
| 	      if (!prepare_to_step (0))	/* set up for single-step */
 | |
| 		{
 | |
| 		  /* prepare_to_step has already emulated the target insn:
 | |
| 		     Send SIGTRAP to gdb, don't resume the target at all.  */
 | |
| 		  ptr = remcomOutBuffer;
 | |
| 		  *ptr++ = 'T';	/* Simulate stopping with SIGTRAP */
 | |
| 		  *ptr++ = '0';
 | |
| 		  *ptr++ = '5';
 | |
| 
 | |
| 		  *ptr++ = hexchars[PC >> 4];	/* send PC */
 | |
| 		  *ptr++ = hexchars[PC & 0xf];
 | |
| 		  *ptr++ = ':';
 | |
| 		  ptr = mem2hex ((unsigned char *) ®isters[PC], ptr, 4, 0);
 | |
| 		  *ptr++ = ';';
 | |
| 
 | |
| 		  *ptr++ = hexchars[R13 >> 4];	/* send FP */
 | |
| 		  *ptr++ = hexchars[R13 & 0xf];
 | |
| 		  *ptr++ = ':';
 | |
| 		  ptr =
 | |
| 		    mem2hex ((unsigned char *) ®isters[R13], ptr, 4, 0);
 | |
| 		  *ptr++ = ';';
 | |
| 
 | |
| 		  *ptr++ = hexchars[R15 >> 4];	/* send SP */
 | |
| 		  *ptr++ = hexchars[R15 & 0xf];
 | |
| 		  *ptr++ = ':';
 | |
| 		  ptr =
 | |
| 		    mem2hex ((unsigned char *) ®isters[R15], ptr, 4, 0);
 | |
| 		  *ptr++ = ';';
 | |
| 		  *ptr++ = 0;
 | |
| 
 | |
| 		  break;
 | |
| 		}
 | |
| 	    }
 | |
| 	  else			/* continuing, not single-stepping */
 | |
| 	    {
 | |
| 	      /* OK, about to do a "continue".  First check to see if the 
 | |
| 	         target pc is on an odd boundary (second instruction in the 
 | |
| 	         word).  If so, we must do a single-step first, because 
 | |
| 	         ya can't jump or return back to an odd boundary!  */
 | |
| 	      if ((registers[PC] & 2) != 0)
 | |
| 		prepare_to_step (1);
 | |
| 	    }
 | |
| 
 | |
| 	  return;
 | |
| 
 | |
| 	case 'D':		/* Detach */
 | |
| #if 0
 | |
| 	  /* I am interpreting this to mean, release the board from control 
 | |
| 	     by the remote stub.  To do this, I am restoring the original
 | |
| 	     (or at least previous) exception vectors.
 | |
| 	   */
 | |
| 	  for (i = 0; i < 18; i++)
 | |
| 	    exceptionHandler (i, save_vectors[i]);
 | |
| 	  putpacket ("OK");
 | |
| 	  return;		/* continue the inferior */
 | |
| #else
 | |
| 	  strcpy (remcomOutBuffer, "OK");
 | |
| 	  break;
 | |
| #endif
 | |
| 	case 'q':
 | |
| 	  if (*ptr++ == 'C' &&
 | |
| 	      *ptr++ == 'R' && *ptr++ == 'C' && *ptr++ == ':')
 | |
| 	    {
 | |
| 	      unsigned long start, len, our_crc;
 | |
| 
 | |
| 	      if (hexToInt (&ptr, (int *) &start) &&
 | |
| 		  *ptr++ == ',' && hexToInt (&ptr, (int *) &len))
 | |
| 		{
 | |
| 		  remcomOutBuffer[0] = 'C';
 | |
| 		  our_crc = crc32 ((unsigned char *) start, len, 0xffffffff);
 | |
| 		  mem2hex ((char *) &our_crc,
 | |
| 			   &remcomOutBuffer[1], sizeof (long), 0);
 | |
| 		}		/* else do nothing */
 | |
| 	    }			/* else do nothing */
 | |
| 	  break;
 | |
| 
 | |
| 	case 'k':		/* kill the program */
 | |
| 	  continue;
 | |
| 	}			/* switch */
 | |
| 
 | |
|       /* reply to the request */
 | |
|       putpacket (remcomOutBuffer);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* qCRC support */
 | |
| 
 | |
| /* Table used by the crc32 function to calcuate the checksum. */
 | |
| static unsigned long crc32_table[256] = { 0, 0 };
 | |
| 
 | |
| static unsigned long
 | |
| crc32 (unsigned char *buf, int len, unsigned long crc)
 | |
| {
 | |
|   if (!crc32_table[1])
 | |
|     {
 | |
|       /* Initialize the CRC table and the decoding table. */
 | |
|       int i, j;
 | |
|       unsigned long c;
 | |
| 
 | |
|       for (i = 0; i < 256; i++)
 | |
| 	{
 | |
| 	  for (c = i << 24, j = 8; j > 0; --j)
 | |
| 	    c = c & 0x80000000 ? (c << 1) ^ 0x04c11db7 : (c << 1);
 | |
| 	  crc32_table[i] = c;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   while (len--)
 | |
|     {
 | |
|       crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *buf) & 255];
 | |
|       buf++;
 | |
|     }
 | |
|   return crc;
 | |
| }
 | |
| 
 | |
| static int
 | |
| hex (unsigned char ch)
 | |
| {
 | |
|   if ((ch >= 'a') && (ch <= 'f'))
 | |
|     return (ch - 'a' + 10);
 | |
|   if ((ch >= '0') && (ch <= '9'))
 | |
|     return (ch - '0');
 | |
|   if ((ch >= 'A') && (ch <= 'F'))
 | |
|     return (ch - 'A' + 10);
 | |
|   return (-1);
 | |
| }
 | |
| 
 | |
| /* scan for the sequence $<data>#<checksum>     */
 | |
| 
 | |
| unsigned char *
 | |
| getpacket (void)
 | |
| {
 | |
|   unsigned char *buffer = &remcomInBuffer[0];
 | |
|   unsigned char checksum;
 | |
|   unsigned char xmitcsum;
 | |
|   int count;
 | |
|   char ch;
 | |
| 
 | |
|   while (1)
 | |
|     {
 | |
|       /* wait around for the start character, ignore all other characters */
 | |
|       while ((ch = getDebugChar ()) != '$')
 | |
| 	;
 | |
| 
 | |
|     retry:
 | |
|       checksum = 0;
 | |
|       xmitcsum = -1;
 | |
|       count = 0;
 | |
| 
 | |
|       /* now, read until a # or end of buffer is found */
 | |
|       while (count < BUFMAX - 1)
 | |
| 	{
 | |
| 	  ch = getDebugChar ();
 | |
| 	  if (ch == '$')
 | |
| 	    goto retry;
 | |
| 	  if (ch == '#')
 | |
| 	    break;
 | |
| 	  checksum = checksum + ch;
 | |
| 	  buffer[count] = ch;
 | |
| 	  count = count + 1;
 | |
| 	}
 | |
|       buffer[count] = 0;
 | |
| 
 | |
|       if (ch == '#')
 | |
| 	{
 | |
| 	  ch = getDebugChar ();
 | |
| 	  xmitcsum = hex (ch) << 4;
 | |
| 	  ch = getDebugChar ();
 | |
| 	  xmitcsum += hex (ch);
 | |
| 
 | |
| 	  if (checksum != xmitcsum)
 | |
| 	    {
 | |
| 	      if (remote_debug)
 | |
| 		{
 | |
| 		  unsigned char buf[16];
 | |
| 
 | |
| 		  mem2hex ((unsigned char *) &checksum, buf, 4, 0);
 | |
| 		  gdb_error ("Bad checksum: my count = %s, ", buf);
 | |
| 		  mem2hex ((unsigned char *) &xmitcsum, buf, 4, 0);
 | |
| 		  gdb_error ("sent count = %s\n", buf);
 | |
| 		  gdb_error (" -- Bad buffer: \"%s\"\n", buffer);
 | |
| 		}
 | |
| 	      putDebugChar ('-');	/* failed checksum */
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      putDebugChar ('+');	/* successful transfer */
 | |
| 
 | |
| 	      /* if a sequence char is present, reply the sequence ID */
 | |
| 	      if (buffer[2] == ':')
 | |
| 		{
 | |
| 		  putDebugChar (buffer[0]);
 | |
| 		  putDebugChar (buffer[1]);
 | |
| 
 | |
| 		  return &buffer[3];
 | |
| 		}
 | |
| 
 | |
| 	      return &buffer[0];
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* send the packet in buffer.  */
 | |
| 
 | |
| static void
 | |
| putpacket (unsigned char *buffer)
 | |
| {
 | |
|   unsigned char checksum;
 | |
|   int count;
 | |
|   char ch;
 | |
| 
 | |
|   /*  $<packet info>#<checksum>. */
 | |
|   do
 | |
|     {
 | |
|       putDebugChar ('$');
 | |
|       checksum = 0;
 | |
|       count = 0;
 | |
| 
 | |
|       while (ch = buffer[count])
 | |
| 	{
 | |
| 	  putDebugChar (ch);
 | |
| 	  checksum += ch;
 | |
| 	  count += 1;
 | |
| 	}
 | |
|       putDebugChar ('#');
 | |
|       putDebugChar (hexchars[checksum >> 4]);
 | |
|       putDebugChar (hexchars[checksum % 16]);
 | |
|     }
 | |
|   while (getDebugChar () != '+');
 | |
| }
 | |
| 
 | |
| /* Address of a routine to RTE to if we get a memory fault.  */
 | |
| 
 | |
| static void (*volatile mem_fault_routine) () = 0;
 | |
| 
 | |
| static void
 | |
| set_mem_err (void)
 | |
| {
 | |
|   mem_err = 1;
 | |
| }
 | |
| 
 | |
| /* Check the address for safe access ranges.  As currently defined,
 | |
|    this routine will reject the "expansion bus" address range(s).
 | |
|    To make those ranges useable, someone must implement code to detect
 | |
|    whether there's anything connected to the expansion bus. */
 | |
| 
 | |
| static int
 | |
| mem_safe (unsigned char *addr)
 | |
| {
 | |
| #define BAD_RANGE_ONE_START	((unsigned char *) 0x600000)
 | |
| #define BAD_RANGE_ONE_END	((unsigned char *) 0xa00000)
 | |
| #define BAD_RANGE_TWO_START	((unsigned char *) 0xff680000)
 | |
| #define BAD_RANGE_TWO_END	((unsigned char *) 0xff800000)
 | |
| 
 | |
|   if (addr < BAD_RANGE_ONE_START)
 | |
|     return 1;			/* safe */
 | |
|   if (addr < BAD_RANGE_ONE_END)
 | |
|     return 0;			/* unsafe */
 | |
|   if (addr < BAD_RANGE_TWO_START)
 | |
|     return 1;			/* safe */
 | |
|   if (addr < BAD_RANGE_TWO_END)
 | |
|     return 0;			/* unsafe */
 | |
| }
 | |
| 
 | |
| /* These are separate functions so that they are so short and sweet
 | |
|    that the compiler won't save any registers (if there is a fault
 | |
|    to mem_fault, they won't get restored, so there better not be any
 | |
|    saved).  */
 | |
| static int
 | |
| get_char (unsigned char *addr)
 | |
| {
 | |
| #if 1
 | |
|   if (mem_fault_routine && !mem_safe (addr))
 | |
|     {
 | |
|       mem_fault_routine ();
 | |
|       return 0;
 | |
|     }
 | |
| #endif
 | |
|   return *addr;
 | |
| }
 | |
| 
 | |
| static void
 | |
| set_char (unsigned char *addr, unsigned char val)
 | |
| {
 | |
| #if 1
 | |
|   if (mem_fault_routine && !mem_safe (addr))
 | |
|     {
 | |
|       mem_fault_routine ();
 | |
|       return;
 | |
|     }
 | |
| #endif
 | |
|   *addr = val;
 | |
| }
 | |
| 
 | |
| /* Convert the memory pointed to by mem into hex, placing result in buf.
 | |
|    Return a pointer to the last char put in buf (null).
 | |
|    If MAY_FAULT is non-zero, then we should set mem_err in response to
 | |
|    a fault; if zero treat a fault like any other fault in the stub.  */
 | |
| 
 | |
| static unsigned char *
 | |
| mem2hex (unsigned char *mem, unsigned char *buf, int count, int may_fault)
 | |
| {
 | |
|   int i;
 | |
|   unsigned char ch;
 | |
| 
 | |
|   if (may_fault)
 | |
|     mem_fault_routine = set_mem_err;
 | |
|   for (i = 0; i < count; i++)
 | |
|     {
 | |
|       ch = get_char (mem++);
 | |
|       if (may_fault && mem_err)
 | |
| 	return (buf);
 | |
|       *buf++ = hexchars[ch >> 4];
 | |
|       *buf++ = hexchars[ch % 16];
 | |
|     }
 | |
|   *buf = 0;
 | |
|   if (may_fault)
 | |
|     mem_fault_routine = 0;
 | |
|   return (buf);
 | |
| }
 | |
| 
 | |
| /* Convert the hex array pointed to by buf into binary to be placed in mem.
 | |
|    Return a pointer to the character AFTER the last byte written. */
 | |
| 
 | |
| static unsigned char *
 | |
| hex2mem (unsigned char *buf, unsigned char *mem, int count, int may_fault)
 | |
| {
 | |
|   int i;
 | |
|   unsigned char ch;
 | |
| 
 | |
|   if (may_fault)
 | |
|     mem_fault_routine = set_mem_err;
 | |
|   for (i = 0; i < count; i++)
 | |
|     {
 | |
|       ch = hex (*buf++) << 4;
 | |
|       ch = ch + hex (*buf++);
 | |
|       set_char (mem++, ch);
 | |
|       if (may_fault && mem_err)
 | |
| 	return (mem);
 | |
|     }
 | |
|   if (may_fault)
 | |
|     mem_fault_routine = 0;
 | |
|   return (mem);
 | |
| }
 | |
| 
 | |
| /* Convert the binary stream in BUF to memory.
 | |
| 
 | |
|    Gdb will escape $, #, and the escape char (0x7d).
 | |
|    COUNT is the total number of bytes to write into
 | |
|    memory. */
 | |
| static unsigned char *
 | |
| bin2mem (unsigned char *buf, unsigned char *mem, int count, int may_fault)
 | |
| {
 | |
|   int i;
 | |
|   unsigned char ch;
 | |
| 
 | |
|   if (may_fault)
 | |
|     mem_fault_routine = set_mem_err;
 | |
|   for (i = 0; i < count; i++)
 | |
|     {
 | |
|       /* Check for any escaped characters. Be paranoid and
 | |
|          only unescape chars that should be escaped. */
 | |
|       if (*buf == 0x7d)
 | |
| 	{
 | |
| 	  switch (*(buf + 1))
 | |
| 	    {
 | |
| 	    case 0x3:		/* # */
 | |
| 	    case 0x4:		/* $ */
 | |
| 	    case 0x5d:		/* escape char */
 | |
| 	      buf++;
 | |
| 	      *buf |= 0x20;
 | |
| 	      break;
 | |
| 	    default:
 | |
| 	      /* nothing */
 | |
| 	      break;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       set_char (mem++, *buf++);
 | |
| 
 | |
|       if (may_fault && mem_err)
 | |
| 	return mem;
 | |
|     }
 | |
| 
 | |
|   if (may_fault)
 | |
|     mem_fault_routine = 0;
 | |
|   return mem;
 | |
| }
 | |
| 
 | |
| /* this function takes the m32r exception vector and attempts to
 | |
|    translate this number into a unix compatible signal value */
 | |
| 
 | |
| static int
 | |
| computeSignal (int exceptionVector)
 | |
| {
 | |
|   int sigval;
 | |
|   switch (exceptionVector)
 | |
|     {
 | |
|     case 0:
 | |
|       sigval = 23;
 | |
|       break;			/* I/O trap                    */
 | |
|     case 1:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 2:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 3:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 4:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 5:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 6:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 7:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 8:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 9:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 10:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 11:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 12:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 13:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 14:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 15:
 | |
|       sigval = 5;
 | |
|       break;			/* breakpoint                  */
 | |
|     case 16:
 | |
|       sigval = 10;
 | |
|       break;			/* BUS ERROR (alignment)       */
 | |
|     case 17:
 | |
|       sigval = 2;
 | |
|       break;			/* INTerrupt                   */
 | |
|     default:
 | |
|       sigval = 7;
 | |
|       break;			/* "software generated"        */
 | |
|     }
 | |
|   return (sigval);
 | |
| }
 | |
| 
 | |
| /**********************************************/
 | |
| /* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
 | |
| /* RETURN NUMBER OF CHARS PROCESSED           */
 | |
| /**********************************************/
 | |
| static int
 | |
| hexToInt (unsigned char **ptr, int *intValue)
 | |
| {
 | |
|   int numChars = 0;
 | |
|   int hexValue;
 | |
| 
 | |
|   *intValue = 0;
 | |
|   while (**ptr)
 | |
|     {
 | |
|       hexValue = hex (**ptr);
 | |
|       if (hexValue >= 0)
 | |
| 	{
 | |
| 	  *intValue = (*intValue << 4) | hexValue;
 | |
| 	  numChars++;
 | |
| 	}
 | |
|       else
 | |
| 	break;
 | |
|       (*ptr)++;
 | |
|     }
 | |
|   return (numChars);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Table of branch instructions:
 | |
|   
 | |
|   10B6		RTE	return from trap or exception
 | |
|   1FCr		JMP	jump
 | |
|   1ECr		JL	jump and link
 | |
|   7Fxx		BRA	branch
 | |
|   FFxxxxxx	BRA	branch (long)
 | |
|   B09rxxxx	BNEZ	branch not-equal-zero
 | |
|   Br1rxxxx	BNE	branch not-equal
 | |
|   7Dxx		BNC	branch not-condition
 | |
|   FDxxxxxx	BNC	branch not-condition (long)
 | |
|   B0Arxxxx	BLTZ	branch less-than-zero
 | |
|   B0Crxxxx	BLEZ	branch less-equal-zero
 | |
|   7Exx		BL	branch and link
 | |
|   FExxxxxx	BL	branch and link (long)
 | |
|   B0Drxxxx	BGTZ	branch greater-than-zero
 | |
|   B0Brxxxx	BGEZ	branch greater-equal-zero
 | |
|   B08rxxxx	BEQZ	branch equal-zero
 | |
|   Br0rxxxx	BEQ	branch equal
 | |
|   7Cxx		BC	branch condition
 | |
|   FCxxxxxx	BC	branch condition (long)
 | |
|   */
 | |
| 
 | |
| static int
 | |
| isShortBranch (unsigned char *instr)
 | |
| {
 | |
|   unsigned char instr0 = instr[0] & 0x7F;	/* mask off high bit */
 | |
| 
 | |
|   if (instr0 == 0x10 && instr[1] == 0xB6)	/* RTE */
 | |
|     return 1;			/* return from trap or exception */
 | |
| 
 | |
|   if (instr0 == 0x1E || instr0 == 0x1F)	/* JL or JMP */
 | |
|     if ((instr[1] & 0xF0) == 0xC0)
 | |
|       return 2;			/* jump thru a register */
 | |
| 
 | |
|   if (instr0 == 0x7C || instr0 == 0x7D ||	/* BC, BNC, BL, BRA */
 | |
|       instr0 == 0x7E || instr0 == 0x7F)
 | |
|     return 3;			/* eight bit PC offset */
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| isLongBranch (unsigned char *instr)
 | |
| {
 | |
|   if (instr[0] == 0xFC || instr[0] == 0xFD ||	/* BRA, BNC, BL, BC */
 | |
|       instr[0] == 0xFE || instr[0] == 0xFF)	/* 24 bit relative */
 | |
|     return 4;
 | |
|   if ((instr[0] & 0xF0) == 0xB0)	/* 16 bit relative */
 | |
|     {
 | |
|       if ((instr[1] & 0xF0) == 0x00 ||	/* BNE, BEQ */
 | |
| 	  (instr[1] & 0xF0) == 0x10)
 | |
| 	return 5;
 | |
|       if (instr[0] == 0xB0)	/* BNEZ, BLTZ, BLEZ, BGTZ, BGEZ, BEQZ */
 | |
| 	if ((instr[1] & 0xF0) == 0x80 || (instr[1] & 0xF0) == 0x90 ||
 | |
| 	    (instr[1] & 0xF0) == 0xA0 || (instr[1] & 0xF0) == 0xB0 ||
 | |
| 	    (instr[1] & 0xF0) == 0xC0 || (instr[1] & 0xF0) == 0xD0)
 | |
| 	  return 6;
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* if address is NOT on a 4-byte boundary, or high-bit of instr is zero, 
 | |
|    then it's a 2-byte instruction, else it's a 4-byte instruction.  */
 | |
| 
 | |
| #define INSTRUCTION_SIZE(addr) \
 | |
|     ((((int) addr & 2) || (((unsigned char *) addr)[0] & 0x80) == 0) ? 2 : 4)
 | |
| 
 | |
| static int
 | |
| isBranch (unsigned char *instr)
 | |
| {
 | |
|   if (INSTRUCTION_SIZE (instr) == 2)
 | |
|     return isShortBranch (instr);
 | |
|   else
 | |
|     return isLongBranch (instr);
 | |
| }
 | |
| 
 | |
| static int
 | |
| willBranch (unsigned char *instr, int branchCode)
 | |
| {
 | |
|   switch (branchCode)
 | |
|     {
 | |
|     case 0:
 | |
|       return 0;			/* not a branch */
 | |
|     case 1:
 | |
|       return 1;			/* RTE */
 | |
|     case 2:
 | |
|       return 1;			/* JL or JMP    */
 | |
|     case 3:			/* BC, BNC, BL, BRA (short) */
 | |
|     case 4:			/* BC, BNC, BL, BRA (long) */
 | |
|       switch (instr[0] & 0x0F)
 | |
| 	{
 | |
| 	case 0xC:		/* Branch if Condition Register */
 | |
| 	  return (registers[CBR] != 0);
 | |
| 	case 0xD:		/* Branch if NOT Condition Register */
 | |
| 	  return (registers[CBR] == 0);
 | |
| 	case 0xE:		/* Branch and Link */
 | |
| 	case 0xF:		/* Branch (unconditional) */
 | |
| 	  return 1;
 | |
| 	default:		/* oops? */
 | |
| 	  return 0;
 | |
| 	}
 | |
|     case 5:			/* BNE, BEQ */
 | |
|       switch (instr[1] & 0xF0)
 | |
| 	{
 | |
| 	case 0x00:		/* Branch if r1 equal to r2 */
 | |
| 	  return (registers[instr[0] & 0x0F] == registers[instr[1] & 0x0F]);
 | |
| 	case 0x10:		/* Branch if r1 NOT equal to r2 */
 | |
| 	  return (registers[instr[0] & 0x0F] != registers[instr[1] & 0x0F]);
 | |
| 	default:		/* oops? */
 | |
| 	  return 0;
 | |
| 	}
 | |
|     case 6:			/* BNEZ, BLTZ, BLEZ, BGTZ, BGEZ ,BEQZ */
 | |
|       switch (instr[1] & 0xF0)
 | |
| 	{
 | |
| 	case 0x80:		/* Branch if reg equal to zero */
 | |
| 	  return (registers[instr[1] & 0x0F] == 0);
 | |
| 	case 0x90:		/* Branch if reg NOT equal to zero */
 | |
| 	  return (registers[instr[1] & 0x0F] != 0);
 | |
| 	case 0xA0:		/* Branch if reg less than zero */
 | |
| 	  return (registers[instr[1] & 0x0F] < 0);
 | |
| 	case 0xB0:		/* Branch if reg greater or equal to zero */
 | |
| 	  return (registers[instr[1] & 0x0F] >= 0);
 | |
| 	case 0xC0:		/* Branch if reg less than or equal to zero */
 | |
| 	  return (registers[instr[1] & 0x0F] <= 0);
 | |
| 	case 0xD0:		/* Branch if reg greater than zero */
 | |
| 	  return (registers[instr[1] & 0x0F] > 0);
 | |
| 	default:		/* oops? */
 | |
| 	  return 0;
 | |
| 	}
 | |
|     default:			/* oops? */
 | |
|       return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int
 | |
| branchDestination (unsigned char *instr, int branchCode)
 | |
| {
 | |
|   switch (branchCode)
 | |
|     {
 | |
|     default:
 | |
|     case 0:			/* not a branch */
 | |
|       return 0;
 | |
|     case 1:			/* RTE */
 | |
|       return registers[BPC] & ~3;	/* pop BPC into PC */
 | |
|     case 2:			/* JL or JMP */
 | |
|       return registers[instr[1] & 0x0F] & ~3;	/* jump thru a register */
 | |
|     case 3:			/* BC, BNC, BL, BRA (short, 8-bit relative offset) */
 | |
|       return (((int) instr) & ~3) + ((char) instr[1] << 2);
 | |
|     case 4:			/* BC, BNC, BL, BRA (long, 24-bit relative offset) */
 | |
|       return ((int) instr +
 | |
| 	      ((((char) instr[1] << 16) | (instr[2] << 8) | (instr[3])) <<
 | |
| 	       2));
 | |
|     case 5:			/* BNE, BEQ (16-bit relative offset) */
 | |
|     case 6:			/* BNEZ, BLTZ, BLEZ, BGTZ, BGEZ ,BEQZ (ditto) */
 | |
|       return ((int) instr + ((((char) instr[2] << 8) | (instr[3])) << 2));
 | |
|     }
 | |
| 
 | |
|   /* An explanatory note: in the last three return expressions, I have
 | |
|      cast the most-significant byte of the return offset to char.
 | |
|      What this accomplishes is sign extension.  If the other
 | |
|      less-significant bytes were signed as well, they would get sign
 | |
|      extended too and, if negative, their leading bits would clobber
 | |
|      the bits of the more-significant bytes ahead of them.  There are
 | |
|      other ways I could have done this, but sign extension from
 | |
|      odd-sized integers is always a pain. */
 | |
| }
 | |
| 
 | |
| static void
 | |
| branchSideEffects (unsigned char *instr, int branchCode)
 | |
| {
 | |
|   switch (branchCode)
 | |
|     {
 | |
|     case 1:			/* RTE */
 | |
|       return;			/* I <THINK> this is already handled... */
 | |
|     case 2:			/* JL (or JMP) */
 | |
|     case 3:			/* BL (or BC, BNC, BRA) */
 | |
|     case 4:
 | |
|       if ((instr[0] & 0x0F) == 0x0E)	/* branch/jump and link */
 | |
| 	registers[R14] = (registers[PC] & ~3) + 4;
 | |
|       return;
 | |
|     default:			/* any other branch has no side effects */
 | |
|       return;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static struct STEPPING_CONTEXT
 | |
| {
 | |
|   int stepping;			/* true when we've started a single-step */
 | |
|   unsigned long target_addr;	/* the instr we're trying to execute */
 | |
|   unsigned long target_size;	/* the size of the target instr */
 | |
|   unsigned long noop_addr;	/* where we've inserted a no-op, if any */
 | |
|   unsigned long trap1_addr;	/* the trap following the target instr */
 | |
|   unsigned long trap2_addr;	/* the trap at a branch destination, if any */
 | |
|   unsigned short noop_save;	/* instruction overwritten by our no-op */
 | |
|   unsigned short trap1_save;	/* instruction overwritten by trap1 */
 | |
|   unsigned short trap2_save;	/* instruction overwritten by trap2 */
 | |
|   unsigned short continue_p;	/* true if NOT returning to gdb after step */
 | |
| } stepping;
 | |
| 
 | |
| /* Function: prepare_to_step
 | |
|    Called from handle_exception to prepare the user program to single-step.
 | |
|    Places a trap instruction after the target instruction, with special 
 | |
|    extra handling for branch instructions and for instructions in the 
 | |
|    second half-word of a word.  
 | |
| 
 | |
|    Returns: True  if we should actually execute the instruction; 
 | |
| 	    False if we are going to emulate executing the instruction,
 | |
| 	    in which case we simply report to GDB that the instruction 
 | |
| 	    has already been executed.  */
 | |
| 
 | |
| #define TRAP1  0x10f1;		/* trap #1 instruction */
 | |
| #define NOOP   0x7000;		/* noop    instruction */
 | |
| 
 | |
| static unsigned short trap1 = TRAP1;
 | |
| static unsigned short noop = NOOP;
 | |
| 
 | |
| static int
 | |
| prepare_to_step (continue_p)
 | |
|      int continue_p;		/* if this isn't REALLY a single-step (see below) */
 | |
| {
 | |
|   unsigned long pc = registers[PC];
 | |
|   int branchCode = isBranch ((unsigned char *) pc);
 | |
|   unsigned char *p;
 | |
| 
 | |
|   /* zero out the stepping context 
 | |
|      (paranoia -- it should already be zeroed) */
 | |
|   for (p = (unsigned char *) &stepping;
 | |
|        p < ((unsigned char *) &stepping) + sizeof (stepping); p++)
 | |
|     *p = 0;
 | |
| 
 | |
|   if (branchCode != 0)		/* next instruction is a branch */
 | |
|     {
 | |
|       branchSideEffects ((unsigned char *) pc, branchCode);
 | |
|       if (willBranch ((unsigned char *) pc, branchCode))
 | |
| 	registers[PC] = branchDestination ((unsigned char *) pc, branchCode);
 | |
|       else
 | |
| 	registers[PC] = pc + INSTRUCTION_SIZE (pc);
 | |
|       return 0;			/* branch "executed" -- just notify GDB */
 | |
|     }
 | |
|   else if (((int) pc & 2) != 0)	/* "second-slot" instruction */
 | |
|     {
 | |
|       /* insert no-op before pc */
 | |
|       stepping.noop_addr = pc - 2;
 | |
|       stepping.noop_save = *(unsigned short *) stepping.noop_addr;
 | |
|       *(unsigned short *) stepping.noop_addr = noop;
 | |
|       /* insert trap  after  pc */
 | |
|       stepping.trap1_addr = pc + 2;
 | |
|       stepping.trap1_save = *(unsigned short *) stepping.trap1_addr;
 | |
|       *(unsigned short *) stepping.trap1_addr = trap1;
 | |
|     }
 | |
|   else				/* "first-slot" instruction */
 | |
|     {
 | |
|       /* insert trap  after  pc */
 | |
|       stepping.trap1_addr = pc + INSTRUCTION_SIZE (pc);
 | |
|       stepping.trap1_save = *(unsigned short *) stepping.trap1_addr;
 | |
|       *(unsigned short *) stepping.trap1_addr = trap1;
 | |
|     }
 | |
|   /* "continue_p" means that we are actually doing a continue, and not 
 | |
|      being requested to single-step by GDB.  Sometimes we have to do
 | |
|      one single-step before continuing, because the PC is on a half-word
 | |
|      boundary.  There's no way to simply resume at such an address.  */
 | |
|   stepping.continue_p = continue_p;
 | |
|   stepping.stepping = 1;	/* starting a single-step */
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| /* Function: finish_from_step
 | |
|    Called from handle_exception to finish up when the user program 
 | |
|    returns from a single-step.  Replaces the instructions that had
 | |
|    been overwritten by traps or no-ops, 
 | |
| 
 | |
|    Returns: True  if we should notify GDB that the target stopped.
 | |
| 	    False if we only single-stepped because we had to before we
 | |
| 	    could continue (ie. we were trying to continue at a 
 | |
| 	    half-word boundary).  In that case don't notify GDB:
 | |
| 	    just "continue continuing".  */
 | |
| 
 | |
| static int
 | |
| finish_from_step (void)
 | |
| {
 | |
|   if (stepping.stepping)	/* anything to do? */
 | |
|     {
 | |
|       int continue_p = stepping.continue_p;
 | |
|       unsigned char *p;
 | |
| 
 | |
|       if (stepping.noop_addr)	/* replace instr "under" our no-op */
 | |
| 	*(unsigned short *) stepping.noop_addr = stepping.noop_save;
 | |
|       if (stepping.trap1_addr)	/* replace instr "under" our trap  */
 | |
| 	*(unsigned short *) stepping.trap1_addr = stepping.trap1_save;
 | |
|       if (stepping.trap2_addr)	/* ditto our other trap, if any    */
 | |
| 	*(unsigned short *) stepping.trap2_addr = stepping.trap2_save;
 | |
| 
 | |
|       for (p = (unsigned char *) &stepping;	/* zero out the stepping context */
 | |
| 	   p < ((unsigned char *) &stepping) + sizeof (stepping); p++)
 | |
| 	*p = 0;
 | |
| 
 | |
|       return !(continue_p);
 | |
|     }
 | |
|   else				/* we didn't single-step, therefore this must be a legitimate stop */
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| struct PSWreg
 | |
| {				/* separate out the bit flags in the PSW register */
 | |
|   int pad1:16;
 | |
|   int bsm:1;
 | |
|   int bie:1;
 | |
|   int pad2:5;
 | |
|   int bc:1;
 | |
|   int sm:1;
 | |
|   int ie:1;
 | |
|   int pad3:5;
 | |
|   int c:1;
 | |
| } *psw;
 | |
| 
 | |
| /* Upon entry the value for LR to save has been pushed.
 | |
|    We unpush that so that the value for the stack pointer saved is correct.
 | |
|    Upon entry, all other registers are assumed to have not been modified
 | |
|    since the interrupt/trap occured.  */
 | |
| 
 | |
| asm ("\n\
 | |
| stash_registers:\n\
 | |
| 	push r0\n\
 | |
| 	push r1\n\
 | |
| 	seth r1, #shigh(registers)\n\
 | |
| 	add3 r1, r1, #low(registers)\n\
 | |
| 	pop r0		; r1\n\
 | |
| 	st r0, @(4,r1)\n\
 | |
| 	pop r0		; r0\n\
 | |
| 	st r0, @r1\n\
 | |
| 	addi r1, #4	; only add 4 as subsequent saves are `pre inc'\n\
 | |
| 	st r2, @+r1\n\
 | |
| 	st r3, @+r1\n\
 | |
| 	st r4, @+r1\n\
 | |
| 	st r5, @+r1\n\
 | |
| 	st r6, @+r1\n\
 | |
| 	st r7, @+r1\n\
 | |
| 	st r8, @+r1\n\
 | |
| 	st r9, @+r1\n\
 | |
| 	st r10, @+r1\n\
 | |
| 	st r11, @+r1\n\
 | |
| 	st r12, @+r1\n\
 | |
| 	st r13, @+r1    ; fp\n\
 | |
| 	pop r0		; lr (r14)\n\
 | |
| 	st r0, @+r1\n\
 | |
| 	st sp, @+r1	; sp contains right value at this point\n\
 | |
| 	mvfc r0, cr0\n\
 | |
| 	st r0, @+r1	; cr0 == PSW\n\
 | |
| 	mvfc r0, cr1\n\
 | |
| 	st r0, @+r1	; cr1 == CBR\n\
 | |
| 	mvfc r0, cr2\n\
 | |
| 	st r0, @+r1	; cr2 == SPI\n\
 | |
| 	mvfc r0, cr3\n\
 | |
| 	st r0, @+r1	; cr3 == SPU\n\
 | |
| 	mvfc r0, cr6\n\
 | |
| 	st r0, @+r1	; cr6 == BPC\n\
 | |
| 	st r0, @+r1	; PC  == BPC\n\
 | |
| 	mvfaclo r0\n\
 | |
| 	st r0, @+r1	; ACCL\n\
 | |
| 	mvfachi r0\n\
 | |
| 	st r0, @+r1	; ACCH\n\
 | |
| 	jmp lr");
 | |
| 
 | |
| /* C routine to clean up what stash_registers did.
 | |
|    It is called after calling stash_registers.
 | |
|    This is separate from stash_registers as we want to do this in C
 | |
|    but doing stash_registers in C isn't straightforward.  */
 | |
| 
 | |
| static void
 | |
| cleanup_stash (void)
 | |
| {
 | |
|   psw = (struct PSWreg *) ®isters[PSW];	/* fields of PSW register */
 | |
|   psw->sm = psw->bsm;		/* fix up pre-trap values of psw fields */
 | |
|   psw->ie = psw->bie;
 | |
|   psw->c = psw->bc;
 | |
|   registers[CBR] = psw->bc;	/* fix up pre-trap "C" register */
 | |
| 
 | |
| #if 0				/* FIXME: Was in previous version.  Necessary?
 | |
| 				   (Remember that we use the "rte" insn to return from the
 | |
| 				   trap/interrupt so the values of bsm, bie, bc are important.  */
 | |
|   psw->bsm = psw->bie = psw->bc = 0;	/* zero post-trap values */
 | |
| #endif
 | |
| 
 | |
|   /* FIXME: Copied from previous version.  This can probably be deleted
 | |
|      since methinks stash_registers has already done this.  */
 | |
|   registers[PC] = registers[BPC];	/* pre-trap PC */
 | |
| 
 | |
|   /* FIXME: Copied from previous version.  Necessary?  */
 | |
|   if (psw->sm)			/* copy R15 into (psw->sm ? SPU : SPI) */
 | |
|     registers[SPU] = registers[R15];
 | |
|   else
 | |
|     registers[SPI] = registers[R15];
 | |
| }
 | |
| 
 | |
| asm ("\n\
 | |
| restore_and_return:\n\
 | |
| 	seth r0, #shigh(registers+8)\n\
 | |
| 	add3 r0, r0, #low(registers+8)\n\
 | |
| 	ld r2, @r0+	; restore r2\n\
 | |
| 	ld r3, @r0+	; restore r3\n\
 | |
| 	ld r4, @r0+	; restore r4\n\
 | |
| 	ld r5, @r0+	; restore r5\n\
 | |
| 	ld r6, @r0+	; restore r6\n\
 | |
| 	ld r7, @r0+	; restore r7\n\
 | |
| 	ld r8, @r0+	; restore r8\n\
 | |
| 	ld r9, @r0+	; restore r9\n\
 | |
| 	ld r10, @r0+	; restore r10\n\
 | |
| 	ld r11, @r0+	; restore r11\n\
 | |
| 	ld r12, @r0+	; restore r12\n\
 | |
| 	ld r13, @r0+	; restore r13\n\
 | |
| 	ld r14, @r0+	; restore r14\n\
 | |
| 	ld r15, @r0+	; restore r15\n\
 | |
| 	ld r1, @r0+	; restore cr0 == PSW\n\
 | |
| 	mvtc r1, cr0\n\
 | |
| 	ld r1, @r0+	; restore cr1 == CBR (no-op, because it's read only)\n\
 | |
| 	mvtc r1, cr1\n\
 | |
| 	ld r1, @r0+	; restore cr2 == SPI\n\
 | |
| 	mvtc r1, cr2\n\
 | |
| 	ld r1, @r0+	; restore cr3 == SPU\n\
 | |
| 	mvtc r1, cr3\n\
 | |
| 	addi r0, #4	; skip BPC\n\
 | |
| 	ld r1, @r0+	; restore cr6 (BPC) == PC\n\
 | |
| 	mvtc r1, cr6\n\
 | |
| 	ld r1, @r0+	; restore ACCL\n\
 | |
| 	mvtaclo r1\n\
 | |
| 	ld r1, @r0+	; restore ACCH\n\
 | |
| 	mvtachi r1\n\
 | |
| 	seth r0, #shigh(registers)\n\
 | |
| 	add3 r0, r0, #low(registers)\n\
 | |
| 	ld r1, @(4,r0)	; restore r1\n\
 | |
| 	ld r0, @r0	; restore r0\n\
 | |
| 	rte");
 | |
| 
 | |
| /* General trap handler, called after the registers have been stashed.
 | |
|    NUM is the trap/exception number.  */
 | |
| 
 | |
| static void
 | |
| process_exception (int num)
 | |
| {
 | |
|   cleanup_stash ();
 | |
|   asm volatile ("\n\
 | |
| 	seth r1, #shigh(stackPtr)\n\
 | |
| 	add3 r1, r1, #low(stackPtr)\n\
 | |
| 	ld r15, @r1		; setup local stack (protect user stack)\n\
 | |
| 	mv r0, %0\n\
 | |
| 	bl handle_exception\n\
 | |
| 	bl restore_and_return"::"r" (num):"r0", "r1");
 | |
| }
 | |
| 
 | |
| void _catchException0 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException0:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #0\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException1 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException1:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	bl cleanup_stash\n\
 | |
| 	seth r1, #shigh(stackPtr)\n\
 | |
| 	add3 r1, r1, #low(stackPtr)\n\
 | |
| 	ld r15, @r1		; setup local stack (protect user stack)\n\
 | |
| 	seth r1, #shigh(registers + 21*4) ; PC\n\
 | |
| 	add3 r1, r1, #low(registers + 21*4)\n\
 | |
| 	ld r0, @r1\n\
 | |
| 	addi r0, #-4		; back up PC for breakpoint trap.\n\
 | |
| 	st r0, @r1		; FIXME: what about bp in right slot?\n\
 | |
| 	ldi r0, #1\n\
 | |
| 	bl handle_exception\n\
 | |
| 	bl restore_and_return");
 | |
| 
 | |
| void _catchException2 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException2:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #2\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException3 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException3:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #3\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException4 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException4:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #4\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException5 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException5:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #5\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException6 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException6:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #6\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException7 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException7:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #7\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException8 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException8:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #8\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException9 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException9:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #9\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException10 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException10:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #10\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException11 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException11:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #11\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException12 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException12:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #12\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException13 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException13:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #13\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException14 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException14:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #14\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException15 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException15:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #15\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException16 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException16:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #16\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| void _catchException17 ();
 | |
| 
 | |
| asm ("\n\
 | |
| _catchException17:\n\
 | |
| 	push lr\n\
 | |
| 	bl stash_registers\n\
 | |
| 	; Note that at this point the pushed value of `lr' has been popped\n\
 | |
| 	ldi r0, #17\n\
 | |
| 	bl process_exception");
 | |
| 
 | |
| 
 | |
| /* this function is used to set up exception handlers for tracing and
 | |
|    breakpoints */
 | |
| void
 | |
| set_debug_traps (void)
 | |
| {
 | |
|   /*  extern void remcomHandler(); */
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < 18; i++)	/* keep a copy of old vectors */
 | |
|     if (save_vectors[i] == 0)	/* only copy them the first time */
 | |
|       save_vectors[i] = getExceptionHandler (i);
 | |
| 
 | |
|   stackPtr = &remcomStack[STACKSIZE / sizeof (int) - 1];
 | |
| 
 | |
|   exceptionHandler (0, _catchException0);
 | |
|   exceptionHandler (1, _catchException1);
 | |
|   exceptionHandler (2, _catchException2);
 | |
|   exceptionHandler (3, _catchException3);
 | |
|   exceptionHandler (4, _catchException4);
 | |
|   exceptionHandler (5, _catchException5);
 | |
|   exceptionHandler (6, _catchException6);
 | |
|   exceptionHandler (7, _catchException7);
 | |
|   exceptionHandler (8, _catchException8);
 | |
|   exceptionHandler (9, _catchException9);
 | |
|   exceptionHandler (10, _catchException10);
 | |
|   exceptionHandler (11, _catchException11);
 | |
|   exceptionHandler (12, _catchException12);
 | |
|   exceptionHandler (13, _catchException13);
 | |
|   exceptionHandler (14, _catchException14);
 | |
|   exceptionHandler (15, _catchException15);
 | |
|   exceptionHandler (16, _catchException16);
 | |
|   /*  exceptionHandler (17, _catchException17); */
 | |
| 
 | |
|   initialized = 1;
 | |
| }
 | |
| 
 | |
| /* This function will generate a breakpoint exception.  It is used at the
 | |
|    beginning of a program to sync up with a debugger and can be used
 | |
|    otherwise as a quick means to stop program execution and "break" into
 | |
|    the debugger. */
 | |
| 
 | |
| #define BREAKPOINT() asm volatile ("	trap #2");
 | |
| 
 | |
| void
 | |
| breakpoint (void)
 | |
| {
 | |
|   if (initialized)
 | |
|     BREAKPOINT ();
 | |
| }
 | |
| 
 | |
| /* STDOUT section:
 | |
|    Stuff pertaining to simulating stdout by sending chars to gdb to be echoed.
 | |
|    Functions: gdb_putchar(char ch)
 | |
|               gdb_puts(char *str)
 | |
|               gdb_write(char *str, int len)
 | |
|               gdb_error(char *format, char *parm)
 | |
| 	      */
 | |
| 
 | |
| /* Function: gdb_putchar(int)
 | |
|    Make gdb write a char to stdout.
 | |
|    Returns: the char */
 | |
| 
 | |
| static int
 | |
| gdb_putchar (int ch)
 | |
| {
 | |
|   char buf[4];
 | |
| 
 | |
|   buf[0] = 'O';
 | |
|   buf[1] = hexchars[ch >> 4];
 | |
|   buf[2] = hexchars[ch & 0x0F];
 | |
|   buf[3] = 0;
 | |
|   putpacket (buf);
 | |
|   return ch;
 | |
| }
 | |
| 
 | |
| /* Function: gdb_write(char *, int)
 | |
|    Make gdb write n bytes to stdout (not assumed to be null-terminated).
 | |
|    Returns: number of bytes written */
 | |
| 
 | |
| static int
 | |
| gdb_write (char *data, int len)
 | |
| {
 | |
|   char *buf, *cpy;
 | |
|   int i;
 | |
| 
 | |
|   buf = remcomOutBuffer;
 | |
|   buf[0] = 'O';
 | |
|   i = 0;
 | |
|   while (i < len)
 | |
|     {
 | |
|       for (cpy = buf + 1;
 | |
| 	   i < len && cpy < buf + sizeof (remcomOutBuffer) - 3; i++)
 | |
| 	{
 | |
| 	  *cpy++ = hexchars[data[i] >> 4];
 | |
| 	  *cpy++ = hexchars[data[i] & 0x0F];
 | |
| 	}
 | |
|       *cpy = 0;
 | |
|       putpacket (buf);
 | |
|     }
 | |
|   return len;
 | |
| }
 | |
| 
 | |
| /* Function: gdb_puts(char *)
 | |
|    Make gdb write a null-terminated string to stdout.
 | |
|    Returns: the length of the string */
 | |
| 
 | |
| static int
 | |
| gdb_puts (char *str)
 | |
| {
 | |
|   return gdb_write (str, strlen (str));
 | |
| }
 | |
| 
 | |
| /* Function: gdb_error(char *, char *)
 | |
|    Send an error message to gdb's stdout.
 | |
|    First string may have 1 (one) optional "%s" in it, which
 | |
|    will cause the optional second string to be inserted.  */
 | |
| 
 | |
| static void
 | |
| gdb_error (char *format, char *parm)
 | |
| {
 | |
|   char buf[400], *cpy;
 | |
|   int len;
 | |
| 
 | |
|   if (remote_debug)
 | |
|     {
 | |
|       if (format && *format)
 | |
| 	len = strlen (format);
 | |
|       else
 | |
| 	return;			/* empty input */
 | |
| 
 | |
|       if (parm && *parm)
 | |
| 	len += strlen (parm);
 | |
| 
 | |
|       for (cpy = buf; *format;)
 | |
| 	{
 | |
| 	  if (format[0] == '%' && format[1] == 's')	/* include second string */
 | |
| 	    {
 | |
| 	      format += 2;	/* advance two chars instead of just one */
 | |
| 	      while (parm && *parm)
 | |
| 		*cpy++ = *parm++;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    *cpy++ = *format++;
 | |
| 	}
 | |
|       *cpy = '\0';
 | |
|       gdb_puts (buf);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static unsigned char *
 | |
| strcpy (unsigned char *dest, const unsigned char *src)
 | |
| {
 | |
|   unsigned char *ret = dest;
 | |
| 
 | |
|   if (dest && src)
 | |
|     {
 | |
|       while (*src)
 | |
| 	*dest++ = *src++;
 | |
|       *dest = 0;
 | |
|     }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| strlen (const unsigned char *src)
 | |
| {
 | |
|   int ret;
 | |
| 
 | |
|   for (ret = 0; *src; src++)
 | |
|     ret++;
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| void
 | |
| exit (code)
 | |
|      int code;
 | |
| {
 | |
|   _exit (code);
 | |
| }
 | |
| 
 | |
| int
 | |
| atexit (void *p)
 | |
| {
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| abort (void)
 | |
| {
 | |
|   _exit (1);
 | |
| }
 | |
| #endif
 |