/* This file is part of GDB. 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 2 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This started out life as code shared between the nindy monitor and GDB. For various reasons, this is no longer true. Eventually, it probably should be merged into remote-nindy.c. */ /****************************************************************************** * * NINDY INTERFACE ROUTINES * * This version of the NINDY interface routines supports NINDY versions * 2.13 and older. The older versions used a hex communication protocol, * instead of the (faster) current binary protocol. These routines have * been renamed by prepending the letter 'O' to their names, to avoid * conflict with the current version. The old versions are kept only for * backward compatibility, and well disappear in a future release. * **************************************************************************/ /* Having these in a separate file from nindy.c is really ugly, and should be merged with nindy.c. */ #include #if 0 #include #include /* Needed by file.h on Sys V */ #include #include #include #include /* Needed on Sys V */ #include "ttycntl.h" #endif #include "defs.h" #include "serial.h" #include "block_io.h" #include "gdb_wait.h" #include "env.h" /* Number of bytes that we send to nindy. I believe this is defined by the protocol (it does not agree with REGISTER_BYTES). */ #define OLD_NINDY_REGISTER_BYTES ((36*4) + (4*8)) extern int quiet; /* 1 => stifle unnecessary messages */ /* tty connected to 960/NINDY board. */ extern struct serial *nindy_serial; static OninStrGet(); /**************************** * * * MISCELLANEOUS UTILTIES * * * ****************************/ /****************************************************************************** * fromhex: * Convert a hex ascii digit h to a binary integer ******************************************************************************/ static int fromhex( h ) int h; { if (h >= '0' && h <= '9'){ h -= '0'; } else if (h >= 'a' && h <= 'f'){ h -= 'a' - 10; } else { h = 0; } return (h & 0xff); } /****************************************************************************** * hexbin: * Convert a string of ASCII hex digits to a string of binary bytes. ******************************************************************************/ static hexbin( n, hexp, binp ) int n; /* Number of bytes to convert (twice this many digits)*/ char *hexp; /* Get hex from here */ char *binp; /* Put binary here */ { while ( n-- ){ *binp++ = (fromhex(*hexp) << 4) | fromhex(*(hexp+1)); hexp += 2; } } /****************************************************************************** * binhex: * Convert a string of binary bytes to a string of ASCII hex digits ******************************************************************************/ static binhex( n, binp, hexp ) int n; /* Number of bytes to convert */ char *binp; /* Get binary from here */ char *hexp; /* Place hex here */ { static char tohex[] = "0123456789abcdef"; while ( n-- ){ *hexp++ = tohex[ (*binp >> 4) & 0xf ]; *hexp++ = tohex[ *binp & 0xf ]; binp++; } } /****************************************************************************** * byte_order: * If the host byte order is different from 960 byte order (i.e., the * host is big-endian), reverse the bytes in the passed value; otherwise, * return the passed value unchanged. * ******************************************************************************/ static long byte_order( n ) long n; { long rev; int i; static short test = 0x1234; if (*((char *) &test) == 0x12) { /* * Big-endian host, swap the bytes. */ rev = 0; for ( i = 0; i < sizeof(n); i++ ){ rev <<= 8; rev |= n & 0xff; n >>= 8; } n = rev; } return n; } /****************************************************************************** * say: * This is a printf that takes at most two arguments (in addition to the * format string) and that outputs nothing if verbose output has been * suppressed. ******************************************************************************/ static say( fmt, arg1, arg2 ) char *fmt; int arg1, arg2; { if ( !quiet ){ printf( fmt, arg1, arg2 ); fflush( stdout ); } } /***************************** * * * LOW-LEVEL COMMUNICATION * * * *****************************/ /* Read a single character from the remote end. */ static int readchar() { /* FIXME: Do we really want to be reading without a timeout? */ return serial_readchar (nindy_serial, -1); } /****************************************************************************** * getpkt: * Read a packet from a remote NINDY, with error checking, and return * it in the indicated buffer. ******************************************************************************/ static getpkt (buf) char *buf; { unsigned char recv; /* Checksum received */ unsigned char csum; /* Checksum calculated */ char *bp; /* Poointer into the buffer */ int c; while (1){ csum = 0; bp = buf; /* FIXME: check for error from readchar (). */ while ( (c = readchar()) != '#' ){ *bp++ = c; csum += c; } *bp = 0; /* FIXME: check for error from readchar (). */ recv = fromhex(readchar()) << 4; recv |= fromhex(readchar()); if ( csum == recv ){ break; } fprintf(stderr, "Bad checksum (recv=0x%02x; calc=0x%02x); retrying\r\n", recv, csum ); serial_write (nindy_serial, "-", 1); } serial_write (nindy_serial, "+", 1); } /****************************************************************************** * putpkt: * Checksum and send a gdb command to a remote NINDY, and wait for * positive acknowledgement. * ******************************************************************************/ static putpkt( cmd ) char *cmd; /* Command to be sent, without lead ^P (\020) * or trailing checksum */ { char ack; /* Response received from NINDY */ char checksum[4]; char *p; unsigned int s; char resend; for ( s='\020', p=cmd; *p; p++ ){ s += *p; } sprintf( checksum, "#%02x", s & 0xff ); /* Send checksummed message over and over until we get a positive ack */ resend = 1; do { if ( resend ) { serial_write ( nindy_serial, "\020", 1 ); serial_write( nindy_serial, cmd, strlen(cmd) ); serial_write( nindy_serial, checksum, strlen(checksum) ); } /* FIXME: do we really want to be reading without timeout? */ ack = serial_readchar (nindy_serial, -1); if (ack < 0) { fprintf (stderr, "error reading from serial port\n"); } if ( ack == '-' ){ fprintf( stderr, "Remote NAK, resending\r\n" ); resend = 1; } else if ( ack != '+' ){ fprintf( stderr, "Bad ACK, ignored: <%c>\r\n", ack ); resend = 0; } } while ( ack != '+' ); } /****************************************************************************** * send: * Send a message to a remote NINDY and return the reply in the same * buffer (clobbers the input message). Check for error responses * as indicated by the second argument. * ******************************************************************************/ static send( buf, ack_required ) char *buf; /* Message to be sent to NINDY; replaced by * NINDY's response. */ int ack_required; /* 1 means NINDY's response MUST be either "X00" (no * error) or an error code "Xnn". * 0 means the it's OK as long as it doesn't * begin with "Xnn". */ { int errnum; static char *errmsg[] = { "", /* X00 */ "Buffer overflow", /* X01 */ "Unknown command", /* X02 */ "Wrong amount of data to load register(s)", /* X03 */ "Missing command argument(s)", /* X04 */ "Odd number of digits sent to load memory", /* X05 */ "Unknown register name", /* X06 */ "No such memory segment", /* X07 */ "No breakpoint available", /* X08 */ "Can't set requested baud rate", /* X09 */ }; # define NUMERRS ( sizeof(errmsg) / sizeof(errmsg[0]) ) static char err0[] = "NINDY failed to acknowledge command: <%s>\r\n"; static char err1[] = "Unknown error response from NINDY: <%s>\r\n"; static char err2[] = "Error response %s from NINDY: %s\r\n"; putpkt (buf); getpkt (buf); if ( buf[0] != 'X' ){ if ( ack_required ){ fprintf( stderr, err0, buf ); abort(); } } else if ( strcmp(buf,"X00") ){ sscanf( &buf[1], "%x", &errnum ); if ( errnum > NUMERRS ){ fprintf( stderr, err1, buf ); } else{ fprintf( stderr, err2, buf, errmsg[errnum] ); } abort(); } } /********************************** * * * NINDY INTERFACE ROUTINES * * * * ninConnect *MUST* be the first * * one of these routines called. * **********************************/ /****************************************************************************** * ninBptDel: * Ask NINDY to delete the specified type of *hardware* breakpoint at * the specified address. If the 'addr' is -1, all breakpoints of * the specified type are deleted. ******************************************************************************/ OninBptDel( addr, data ) long addr; /* Address in 960 memory */ int data; /* '1' => data bkpt, '0' => instruction breakpoint */ { char buf[100]; if ( addr == -1 ){ sprintf( buf, "b%c", data ? '1' : '0' ); } else { sprintf( buf, "b%c%x", data ? '1' : '0', addr ); } return send( buf, 0 ); } /****************************************************************************** * ninBptSet: * Ask NINDY to set the specified type of *hardware* breakpoint at * the specified address. ******************************************************************************/ OninBptSet( addr, data ) long addr; /* Address in 960 memory */ int data; /* '1' => data bkpt, '0' => instruction breakpoint */ { char buf[100]; sprintf( buf, "B%c%x", data ? '1' : '0', addr ); return send( buf, 0 ); } /****************************************************************************** * ninGdbExit: * Ask NINDY to leave GDB mode and print a NINDY prompt. * Since it'll no longer be in GDB mode, don't wait for a response. ******************************************************************************/ OninGdbExit() { putpkt( "E" ); } /****************************************************************************** * ninGo: * Ask NINDY to start or continue execution of an application program * in it's memory at the current ip. ******************************************************************************/ OninGo( step_flag ) int step_flag; /* 1 => run in single-step mode */ { putpkt( step_flag ? "s" : "c" ); } /****************************************************************************** * ninMemGet: * Read a string of bytes from NINDY's address space (960 memory). ******************************************************************************/ OninMemGet(ninaddr, hostaddr, len) long ninaddr; /* Source address, in the 960 memory space */ char *hostaddr; /* Destination address, in our memory space */ int len; /* Number of bytes to read */ { /* How much do we send at a time? */ #define OLD_NINDY_MEMBYTES 1024 /* Buffer: hex in, binary out */ char buf[2*OLD_NINDY_MEMBYTES+20]; int cnt; /* Number of bytes in next transfer */ for ( ; len > 0; len -= OLD_NINDY_MEMBYTES ){ cnt = len > OLD_NINDY_MEMBYTES ? OLD_NINDY_MEMBYTES : len; sprintf( buf, "m%x,%x", ninaddr, cnt ); send( buf, 0 ); hexbin( cnt, buf, hostaddr ); ninaddr += cnt; hostaddr += cnt; } } /****************************************************************************** * ninMemPut: * Write a string of bytes into NINDY's address space (960 memory). ******************************************************************************/ OninMemPut( destaddr, srcaddr, len ) long destaddr; /* Destination address, in NINDY memory space */ char *srcaddr; /* Source address, in our memory space */ int len; /* Number of bytes to write */ { char buf[2*OLD_NINDY_MEMBYTES+20]; /* Buffer: binary in, hex out */ char *p; /* Pointer into buffer */ int cnt; /* Number of bytes in next transfer */ for ( ; len > 0; len -= OLD_NINDY_MEMBYTES ){ cnt = len > OLD_NINDY_MEMBYTES ? OLD_NINDY_MEMBYTES : len; sprintf( buf, "M%x,", destaddr ); p = buf + strlen(buf); binhex( cnt, srcaddr, p ); *(p+(2*cnt)) = '\0'; send( buf, 1 ); srcaddr += cnt; destaddr += cnt; } } /****************************************************************************** * ninRegGet: * Retrieve the contents of a 960 register, and return them as a long * in host byte order. * * THIS ROUTINE CAN ONLY BE USED TO READ THE LOCAL, GLOBAL, AND * ip/ac/pc/tc REGISTERS. * ******************************************************************************/ long OninRegGet( regname ) char *regname; /* Register name recognized by NINDY, subject to the * above limitations. */ { char buf[200]; long val; sprintf( buf, "u%s", regname ); send( buf, 0 ); hexbin( 4, buf, (char *)&val ); return byte_order(val); } /****************************************************************************** * ninRegPut: * Set the contents of a 960 register. * * THIS ROUTINE CAN ONLY BE USED TO SET THE LOCAL, GLOBAL, AND * ip/ac/pc/tc REGISTERS. * ******************************************************************************/ OninRegPut( regname, val ) char *regname; /* Register name recognized by NINDY, subject to the * above limitations. */ long val; /* New contents of register, in host byte-order */ { char buf[200]; sprintf( buf, "U%s,%08x", regname, byte_order(val) ); send( buf, 1 ); } /****************************************************************************** * ninRegsGet: * Get a dump of the contents of the entire 960 register set. The * individual registers appear in the dump in the following order: * * pfp sp rip r3 r4 r5 r6 r7 * r8 r9 r10 r11 r12 r13 r14 r15 * g0 g1 g2 g3 g4 g5 g6 g7 * g8 g9 g10 g11 g12 g13 g14 fp * pc ac ip tc fp0 fp1 fp2 fp3 * * Each individual register comprises exactly 4 bytes, except for * fp0-fp3, which are 8 bytes. * * WARNING: * Each register value is in 960 (little-endian) byte order. * ******************************************************************************/ OninRegsGet( regp ) char *regp; /* Where to place the register dump */ { char buf[(2*OLD_NINDY_REGISTER_BYTES)+10]; /* Registers in ASCII hex */ strcpy( buf, "r" ); send( buf, 0 ); hexbin( OLD_NINDY_REGISTER_BYTES, buf, regp ); } /****************************************************************************** * ninRegsPut: * Initialize the entire 960 register set to a specified set of values. * The format of the register value data should be the same as that * returned by ninRegsGet. * * WARNING: * Each register value should be in 960 (little-endian) byte order. * ******************************************************************************/ OninRegsPut( regp ) char *regp; /* Pointer to desired values of registers */ { char buf[(2*OLD_NINDY_REGISTER_BYTES)+10]; /* Registers in ASCII hex */ buf[0] = 'R'; binhex( OLD_NINDY_REGISTER_BYTES, regp, buf+1 ); buf[ (2*OLD_NINDY_REGISTER_BYTES)+1 ] = '\0'; send( buf, 1 ); } /****************************************************************************** * ninReset: * Ask NINDY to perform a soft reset; wait for the reset to complete. ******************************************************************************/ OninReset() { putpkt( "X" ); /* FIXME: check for error from readchar (). */ while ( readchar() != '+' ){ ; } } /****************************************************************************** * ninSrq: * Assume NINDY has stopped execution of the 960 application program in * order to process a host service request (srq). Ask NINDY for the * srq arguments, perform the requested service, and send an "srq * complete" message so NINDY will return control to the application. * ******************************************************************************/ OninSrq() { /* FIXME: Imposes arbitrary limits on lengths of pathnames and such. */ char buf[BUFSIZE]; int retcode; unsigned char srqnum; char *p; char *argp; int nargs; int arg[MAX_SRQ_ARGS]; /* Get srq number and arguments */ strcpy( buf, "!" ); send( buf, 0 ); hexbin( 1, buf, (char *)&srqnum ); /* Set up array of pointers the each of the individual * comma-separated args */ nargs=0; argp = p = buf+2; while ( 1 ){ while ( *p != ',' && *p != '\0' ){ p++; } sscanf( argp, "%x", &arg[nargs++] ); if ( *p == '\0' || nargs == MAX_SRQ_ARGS ){ break; } argp = ++p; } /* Process Srq */ switch( srqnum ){ case BS_CLOSE: /* args: file descriptor */ if ( arg[0] > 2 ){ retcode = close( arg[0] ); } else { retcode = 0; } break; case BS_CREAT: /* args: filename, mode */ OninStrGet( arg[0], buf ); retcode = creat(buf,arg[1]); break; case BS_OPEN: /* args: filename, flags, mode */ OninStrGet( arg[0], buf ); retcode = open(buf,arg[1],arg[2]); break; case BS_READ: /* args: file descriptor, buffer, count */ retcode = read(arg[0],buf,arg[2]); if ( retcode > 0 ){ OninMemPut( arg[1], buf, retcode ); } break; case BS_SEEK: /* args: file descriptor, offset, whence */ retcode = lseek(arg[0],arg[1],arg[2]); break; case BS_WRITE: /* args: file descriptor, buffer, count */ OninMemGet( arg[1], buf, arg[2] ); retcode = write(arg[0],buf,arg[2]); break; default: retcode = -1; break; } /* Tell NINDY to continue */ sprintf( buf, "e%x", retcode ); send( buf, 1 ); } /****************************************************************************** * ninStopWhy: * Assume the application program has stopped (i.e., a DLE was received * from NINDY). Ask NINDY for status information describing the * reason for the halt. * * Returns a non-zero value if the user program has exited, 0 otherwise. * Also returns the following information, through passed pointers: * - why: an exit code if program the exited; otherwise the reason * why the program halted (see stop.h for values). * - contents of register ip (little-endian byte order) * - contents of register sp (little-endian byte order) * - contents of register fp (little-endian byte order) ******************************************************************************/ char OninStopWhy( whyp, ipp, fpp, spp ) char *whyp; /* Return the 'why' code through this pointer */ char *ipp; /* Return contents of register ip through this pointer */ char *fpp; /* Return contents of register fp through this pointer */ char *spp; /* Return contents of register sp through this pointer */ { char buf[30]; char stop_exit; strcpy( buf, "?" ); send( buf, 0 ); hexbin( 1, buf, &stop_exit ); hexbin( 1, buf+2, whyp ); hexbin( 4, buf+4, ipp ); hexbin( 4, buf+12, fpp ); hexbin( 4, buf+20, spp ); return stop_exit; } /****************************************************************************** * ninStrGet: * Read a '\0'-terminated string of data out of the 960 memory space. * ******************************************************************************/ static OninStrGet( ninaddr, hostaddr ) unsigned long ninaddr; /* Address of string in NINDY memory space */ char *hostaddr; /* Address of the buffer to which string should * be copied. */ { /* FIXME: seems to be an arbitrary limit on the length of the string. */ char buf[BUFSIZE]; /* String as 2 ASCII hex digits per byte */ int numchars; /* Length of string in bytes. */ sprintf( buf, "\"%x", ninaddr ); send( buf, 0 ); numchars = strlen(buf)/2; hexbin( numchars, buf, hostaddr ); hostaddr[numchars] = '\0'; } #if 0 /* never used. */ /****************************************************************************** * ninVersion: * Ask NINDY for version information about itself. * The information is sent as an ascii string in the form "x.xx,", * where, * x.xx is the version number * is the processor architecture: "KA", "KB", "MC", "CA" * * ******************************************************************************/ int OninVersion( p ) char *p; /* Where to place version string */ { /* FIXME: this is an arbitrary limit on the length of version string. */ char buf[BUFSIZE]; strcpy( buf, "v" ); send( buf, 0 ); strcpy( p, buf ); return strlen( buf ); } #endif