diff options
Diffstat (limited to 'gdb/remote-adapt.c')
-rw-r--r-- | gdb/remote-adapt.c | 1443 |
1 files changed, 1443 insertions, 0 deletions
diff --git a/gdb/remote-adapt.c b/gdb/remote-adapt.c new file mode 100644 index 0000000..b22f180 --- /dev/null +++ b/gdb/remote-adapt.c @@ -0,0 +1,1443 @@ +/* Remote debugging interface for AMD 290*0 Adapt Monitor Version 2.1d18. + Copyright 1990, 1991, 1992 Free Software Foundation, Inc. + Contributed by David Wood at New York University (wood@lab.ultra.nyu.edu). + Adapted from work done at Cygnus Support in remote-eb.c. + +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 is like remote.c but is for an esoteric situation-- + having a 29k board attached to an Adapt inline monitor. + The monitor is connected via serial line to a unix machine + running gdb. + + 3/91 - developed on Sun3 OS 4.1, by David Wood + o - I can't get binary coff to load. + o - I can't get 19200 baud rate to work. + 7/91 o - Freeze mode tracing can be done on a 29050. */ + + + +#include "defs.h" +#include "gdb_string.h" +#include "inferior.h" +#include "wait.h" +#include "value.h" +#include <ctype.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include "terminal.h" +#include "target.h" +#include "gdbcore.h" + +/* This processor is getting rusty but I am trying to keep it + up to date at least with data structure changes. + Activate this block to compile just this file. + */ +#define COMPILE_CHECK 0 +#if COMPILE_CHECK +#define Q_REGNUM 0 +#define VAB_REGNUM 0 +#define CPS_REGNUM 0 +#define IPA_REGNUM 0 +#define IPB_REGNUM 0 +#define GR1_REGNUM 0 +#define LR0_REGNUM 0 +#define IPC_REGNUM 0 +#define CR_REGNUM 0 +#define BP_REGNUM 0 +#define FC_REGNUM 0 +#define INTE_REGNUM 0 +#define EXO_REGNUM 0 +#define GR96_REGNUM 0 +#define NPC_REGNUM +#define FPE_REGNUM 0 +#define PC2_REGNUM 0 +#define FPS_REGNUM 0 +#define ALU_REGNUM 0 +#define LRU_REGNUM 0 +#define TERMINAL int +#define RAW 1 +#define ANYP 1 +extern int a29k_freeze_mode ; +extern int processor_type ; +extern char * processor_name ; +#endif + +/* External data declarations */ +extern int stop_soon_quietly; /* for wait_for_inferior */ + +/* Forward data declarations */ +extern struct target_ops adapt_ops; /* Forward declaration */ + +/* Forward function declarations */ +static void adapt_fetch_registers (); +static void adapt_store_registers (); +static void adapt_close (); +static int adapt_clear_breakpoints(); + +#define FREEZE_MODE (read_register(CPS_REGNUM) && 0x400) +#define USE_SHADOW_PC ((processor_type == a29k_freeze_mode) && FREEZE_MODE) + +/* Can't seem to get binary coff working */ +#define ASCII_COFF /* Adapt will be downloaded with ascii coff */ + +/* FIXME: Replace with `set remotedebug'. */ +#define LOG_FILE "adapt.log" +#if defined (LOG_FILE) +FILE *log_file=NULL; +#endif + +static int timeout = 5; +static char *dev_name; + +/* Descriptor for I/O to remote machine. Initialize it to -1 so that + adapt_open knows that we don't have a file open when the program + starts. */ +int adapt_desc = -1; + +/* stream which is fdopen'd from adapt_desc. Only valid when + adapt_desc != -1. */ +FILE *adapt_stream; + +#define ON 1 +#define OFF 0 +static void +rawmode(desc, turnon) +int desc; +int turnon; +{ + + TERMINAL sg; + + if (desc < 0) + return; + + ioctl (desc, TIOCGETP, &sg); + + if (turnon) { +#ifdef HAVE_TERMIO + sg.c_lflag &= ~(ICANON); +#else + sg.sg_flags |= RAW; +#endif + } else { +#ifdef HAVE_TERMIO + sg.c_lflag |= ICANON; +#else + sg.sg_flags &= ~(RAW); +#endif + } + ioctl (desc, TIOCSETP, &sg); +} + +/* Suck up all the input from the adapt */ +slurp_input() +{ + char buf[8]; + +#ifdef HAVE_TERMIO + /* termio does the timeout for us. */ + while (read (adapt_desc, buf, 8) > 0); +#else + alarm (timeout); + while (read (adapt_desc, buf, 8) > 0); + alarm (0); +#endif +} + +/* Read a character from the remote system, doing all the fancy + timeout stuff. */ +static int +readchar () +{ + char buf; + + buf = '\0'; +#ifdef HAVE_TERMIO + /* termio does the timeout for us. */ + read (adapt_desc, &buf, 1); +#else + alarm (timeout); + if (read (adapt_desc, &buf, 1) < 0) + { + if (errno == EINTR) + error ("Timeout reading from remote system."); + else + perror_with_name ("remote"); + } + alarm (0); +#endif + + if (buf == '\0') + error ("Timeout reading from remote system."); +#if defined (LOG_FILE) + putc (buf & 0x7f, log_file); +#endif + return buf & 0x7f; +} + +/* Keep discarding input from the remote system, until STRING is found. + Let the user break out immediately. */ +static void +expect (string) + char *string; +{ + char *p = string; + + fflush(adapt_stream); + immediate_quit = 1; + while (1) + { + if (readchar() == *p) + { + p++; + if (*p == '\0') + { + immediate_quit = 0; + return; + } + } + else + p = string; + } +} + +/* Keep discarding input until we see the adapt prompt. + + The convention for dealing with the prompt is that you + o give your command + o *then* wait for the prompt. + + Thus the last thing that a procedure does with the serial line + will be an expect_prompt(). Exception: adapt_resume does not + wait for the prompt, because the terminal is being handed over + to the inferior. However, the next thing which happens after that + is a adapt_wait which does wait for the prompt. + Note that this includes abnormal exit, e.g. error(). This is + necessary to prevent getting into states from which we can't + recover. */ +static void +expect_prompt () +{ +#if defined (LOG_FILE) + /* This is a convenient place to do this. The idea is to do it often + enough that we never lose much data if we terminate abnormally. */ + fflush (log_file); +#endif + fflush(adapt_stream); + expect ("\n# "); +} + +/* Get a hex digit from the remote system & return its value. + If ignore_space is nonzero, ignore spaces (not newline, tab, etc). */ +static int +get_hex_digit (ignore_space) + int ignore_space; +{ + int ch; + while (1) + { + ch = readchar (); + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch == ' ' && ignore_space) + ; + else + { + expect_prompt (); + error ("Invalid hex digit from remote system."); + } + } +} + +/* Get a byte from adapt_desc and put it in *BYT. Accept any number + leading spaces. */ +static void +get_hex_byte (byt) + char *byt; +{ + int val; + + val = get_hex_digit (1) << 4; + val |= get_hex_digit (0); + *byt = val; +} + +/* Read a 32-bit hex word from the adapt, preceded by a space */ +static long +get_hex_word() +{ + long val; + int j; + + val = 0; + for (j = 0; j < 8; j++) + val = (val << 4) + get_hex_digit (j == 0); + return val; +} +/* Get N 32-bit hex words from remote, each preceded by a space + and put them in registers starting at REGNO. */ +static void +get_hex_regs (n, regno) + int n; + int regno; +{ + long val; + while (n--) { + val = get_hex_word(); + supply_register(regno++,(char *) &val); + } +} +/* Called when SIGALRM signal sent due to alarm() timeout. */ +#ifndef HAVE_TERMIO + +#ifndef __STDC__ +# ifndef volatile +# define volatile /**/ +# endif +#endif +volatile int n_alarms; + +void +adapt_timer () +{ +#if 0 + if (kiodebug) + printf ("adapt_timer called\n"); +#endif + n_alarms++; +} +#endif + +/* malloc'd name of the program on the remote system. */ +static char *prog_name = NULL; + +/* Number of SIGTRAPs we need to simulate. That is, the next + NEED_ARTIFICIAL_TRAP calls to adapt_wait should just return + SIGTRAP without actually waiting for anything. */ + +static int need_artificial_trap = 0; + +void +adapt_kill(arg,from_tty) +char *arg; +int from_tty; +{ + fprintf (adapt_stream, "K"); + fprintf (adapt_stream, "\r"); + expect_prompt (); +} +/* + * Download a file specified in 'args', to the adapt. + * FIXME: Assumes the file to download is a binary coff file. + */ +static void +adapt_load(args,fromtty) +char *args; +int fromtty; +{ + FILE *fp; + int n; + char buffer[1024]; + + if (!adapt_stream) { + printf_filtered("Adapt not open. Use 'target' command to open adapt\n"); + return; + } + + /* OK, now read in the file. Y=read, C=COFF, T=dTe port + 0=start address. */ + +#ifdef ASCII_COFF /* Ascii coff */ + fprintf (adapt_stream, "YA T,0\r"); + fflush(adapt_stream); /* Just in case */ + /* FIXME: should check args for only 1 argument */ + sprintf(buffer,"cat %s | btoa > /tmp/#adapt-btoa",args); + system(buffer); + fp = fopen("/tmp/#adapt-btoa","r"); + rawmode(adapt_desc,OFF); + while (n=fread(buffer,1,1024,fp)) { + do { n -= write(adapt_desc,buffer,n); } while (n>0); + if (n<0) { perror("writing ascii coff"); break; } + } + fclose(fp); + rawmode(adapt_desc,ON); + system("rm /tmp/#adapt-btoa"); +#else /* Binary coff - can't get it to work .*/ + fprintf (adapt_stream, "YC T,0\r"); + fflush(adapt_stream); /* Just in case */ + if (!(fp = fopen(args,"r"))) { + printf_filtered("Can't open %s\n",args); + return; + } + while (n=fread(buffer,1,512,fp)) { + do { n -= write(adapt_desc,buffer,n); } while (n>0); + if (n<0) { perror("writing ascii coff"); break; } + } + fclose(fp); +#endif + expect_prompt (); /* Skip garbage that comes out */ + fprintf (adapt_stream, "\r"); + expect_prompt (); +} + +/* This is called not only when we first attach, but also when the + user types "run" after having attached. */ +void +adapt_create_inferior (execfile, args, env) + char *execfile; + char *args; + char **env; +{ + int entry_pt; + + if (args && *args) + error ("Can't pass arguments to remote adapt process."); + + if (execfile == 0 || exec_bfd == 0) + error ("No executable file specified"); + + entry_pt = (int) bfd_get_start_address (exec_bfd); + + if (adapt_stream) { + adapt_kill(NULL,NULL); + adapt_clear_breakpoints(); + init_wait_for_inferior (); + /* Clear the input because what the adapt sends back is different + * depending on whether it was running or not. + */ + slurp_input(); /* After this there should be a prompt */ + fprintf(adapt_stream,"\r"); + expect_prompt(); + printf_filtered("Do you want to download '%s' (y/n)? [y] : ",prog_name); + { + char buffer[10]; + gets(buffer); + if (*buffer != 'n') { + adapt_load(prog_name,0); + } + } + +#ifdef NOTDEF + /* Set the PC and wait for a go/cont */ + fprintf (adapt_stream, "G %x,N\r",entry_pt); + printf_filtered("Now use the 'continue' command to start.\n"); + expect_prompt (); +#else + insert_breakpoints (); /* Needed to get correct instruction in cache */ + proceed(entry_pt, TARGET_SIGNAL_DEFAULT, 0); +#endif + + } else { + printf_filtered("Adapt not open yet.\n"); + } +} + +/* Translate baud rates from integers to damn B_codes. Unix should + have outgrown this crap years ago, but even POSIX wouldn't buck it. */ + +#ifndef B19200 +#define B19200 EXTA +#endif +#ifndef B38400 +#define B38400 EXTB +#endif + +static struct {int rate, damn_b;} baudtab[] = { + {0, B0}, + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {-1, -1}, +}; + +static int damn_b (rate) + int rate; +{ + int i; + + for (i = 0; baudtab[i].rate != -1; i++) + if (rate == baudtab[i].rate) return baudtab[i].damn_b; + return B38400; /* Random */ +} + + +/* Open a connection to a remote debugger. + NAME is the filename used for communication, then a space, + then the baud rate. + */ + +static int baudrate = 9600; +static void +adapt_open (name, from_tty) + char *name; + int from_tty; +{ + TERMINAL sg; + unsigned int prl; + char *p; + + /* Find the first whitespace character, it separates dev_name from + prog_name. */ + if (name == 0) + goto erroid; + + for (p = name; + *p != '\0' && !isspace (*p); p++) + ; + if (*p == '\0') +erroid: + error ("\ +Please include the name of the device for the serial port,\n\ +the baud rate, and the name of the program to run on the remote system."); + dev_name = (char*)xmalloc(p - name + 1); + strncpy (dev_name, name, p - name); + dev_name[p - name] = '\0'; + + /* Skip over the whitespace after dev_name */ + for (; isspace (*p); p++) + /*EMPTY*/; + + if (1 != sscanf (p, "%d ", &baudrate)) + goto erroid; + + /* Skip the number and then the spaces */ + for (; isdigit (*p); p++) + /*EMPTY*/; + for (; isspace (*p); p++) + /*EMPTY*/; + + if (prog_name != NULL) + free (prog_name); + prog_name = savestring (p, strlen (p)); + + adapt_close (0); + + adapt_desc = open (dev_name, O_RDWR); + if (adapt_desc < 0) + perror_with_name (dev_name); + ioctl (adapt_desc, TIOCGETP, &sg); +#if ! defined(COMPILE_CHECK) +#ifdef HAVE_TERMIO + sg.c_cc[VMIN] = 0; /* read with timeout. */ + sg.c_cc[VTIME] = timeout * 10; + sg.c_lflag &= ~(ICANON | ECHO); + sg.c_cflag = (sg.c_cflag & ~CBAUD) | damn_b (baudrate); +#else + sg.sg_ispeed = damn_b (baudrate); + sg.sg_ospeed = damn_b (baudrate); + sg.sg_flags |= RAW | ANYP; + sg.sg_flags &= ~ECHO; +#endif + + ioctl (adapt_desc, TIOCSETP, &sg); + adapt_stream = fdopen (adapt_desc, "r+"); +#endif /* compile_check */ + push_target (&adapt_ops); + +#ifndef HAVE_TERMIO +#ifndef NO_SIGINTERRUPT + /* Cause SIGALRM's to make reads fail with EINTR instead of resuming + the read. */ + if (siginterrupt (SIGALRM, 1) != 0) + perror ("adapt_open: error in siginterrupt"); +#endif + + /* Set up read timeout timer. */ + if ((void (*)) signal (SIGALRM, adapt_timer) == (void (*)) -1) + perror ("adapt_open: error in signal"); +#endif + +#if defined (LOG_FILE) + log_file = fopen (LOG_FILE, "w"); + if (log_file == NULL) + perror_with_name (LOG_FILE); +#endif + + /* Put this port into NORMAL mode, send the 'normal' character */ + write(adapt_desc, "", 1); /* Control A */ + write(adapt_desc, "\r", 1); + expect_prompt (); + + /* Hello? Are you there? */ + write (adapt_desc, "\r", 1); + + expect_prompt (); + + /* Clear any break points */ + adapt_clear_breakpoints(); + + /* Print out some stuff, letting the user now what's going on */ + printf_filtered("Connected to an Adapt via %s.\n", dev_name); + /* FIXME: can this restriction be removed? */ + printf_filtered("Remote debugging using virtual addresses works only\n"); + printf_filtered("\twhen virtual addresses map 1:1 to physical addresses.\n"); + if (processor_type != a29k_freeze_mode) { + fprintf_filtered(gdb_stderr, + "Freeze-mode debugging not available, and can only be done on an A29050.\n"); + } +} + +/* Close out all files and local state before this target loses control. */ + +static void +adapt_close (quitting) + int quitting; +{ + + /* Clear any break points */ + adapt_clear_breakpoints(); + + /* Put this port back into REMOTE mode */ + if (adapt_stream) { + fflush(adapt_stream); + sleep(1); /* Let any output make it all the way back */ + write(adapt_desc, "R\r", 2); + } + + /* Due to a bug in Unix, fclose closes not only the stdio stream, + but also the file descriptor. So we don't actually close + adapt_desc. */ + if (adapt_stream) + fclose (adapt_stream); /* This also closes adapt_desc */ + if (adapt_desc >= 0) + /* close (adapt_desc); */ + + /* Do not try to close adapt_desc again, later in the program. */ + adapt_stream = NULL; + adapt_desc = -1; + +#if defined (LOG_FILE) + if (log_file) { + if (ferror (log_file)) + printf_filtered ("Error writing log file.\n"); + if (fclose (log_file) != 0) + printf_filtered ("Error closing log file.\n"); + log_file = NULL; + } +#endif +} + +/* Attach to the target that is already loaded and possibly running */ +static void +adapt_attach (args, from_tty) + char *args; + int from_tty; +{ + + if (from_tty) + printf_filtered ("Attaching to remote program %s.\n", prog_name); + + /* Send the adapt a kill. It is ok if it is not already running */ + fprintf(adapt_stream, "K\r"); fflush(adapt_stream); + expect_prompt(); /* Slurp the echo */ +} + + +/* Terminate the open connection to the remote debugger. + Use this when you want to detach and do something else + with your gdb. */ +void +adapt_detach (args,from_tty) + char *args; + int from_tty; +{ + + if (adapt_stream) { /* Send it on its way (tell it to continue) */ + adapt_clear_breakpoints(); + fprintf(adapt_stream,"G\r"); + } + + pop_target(); /* calls adapt_close to do the real work */ + if (from_tty) + printf_filtered ("Ending remote %s debugging\n", target_shortname); +} + +/* Tell the remote machine to resume. */ + +void +adapt_resume (pid, step, sig) + int pid, step; + enum target_signal sig; +{ + if (step) + { + write (adapt_desc, "t 1,s\r", 6); + /* Wait for the echo. */ + expect ("t 1,s\r\n"); + /* Then comes a line containing the instruction we stepped to. */ + expect ("@"); + /* Then we get the prompt. */ + expect_prompt (); + + /* Force the next adapt_wait to return a trap. Not doing anything + about I/O from the target means that the user has to type + "continue" to see any. FIXME, this should be fixed. */ + need_artificial_trap = 1; + } + else + { + write (adapt_desc, "G\r", 2); + /* Swallow the echo. */ + expect_prompt(); + } +} + +/* Wait until the remote machine stops, then return, + storing status in STATUS just as `wait' would. */ + +int +adapt_wait (status) + struct target_waitstatus *status; +{ + /* Strings to look for. '?' means match any single character. + Note that with the algorithm we use, the initial character + of the string cannot recur in the string, or we will not + find some cases of the string in the input. */ + + static char bpt[] = "@"; + /* It would be tempting to look for "\n[__exit + 0x8]\n" + but that requires loading symbols with "yc i" and even if + we did do that we don't know that the file has symbols. */ + static char exitmsg[] = "@????????I JMPTI GR121,LR0"; + char *bp = bpt; + char *ep = exitmsg; + + /* Large enough for either sizeof (bpt) or sizeof (exitmsg) chars. */ + char swallowed[50]; + /* Current position in swallowed. */ + char *swallowed_p = swallowed; + + int ch; + int ch_handled; + int old_timeout = timeout; + int old_immediate_quit = immediate_quit; + + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = 0; + + if (need_artificial_trap != 0) + { + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + need_artificial_trap--; + return 0; + } + + timeout = 0; /* Don't time out -- user program is running. */ + immediate_quit = 1; /* Helps ability to QUIT */ + while (1) { + QUIT; /* Let user quit and leave process running */ + ch_handled = 0; + ch = readchar (); + if (ch == *bp) { + bp++; + if (*bp == '\0') + break; + ch_handled = 1; + + *swallowed_p++ = ch; + } else + bp = bpt; + if (ch == *ep || *ep == '?') { + ep++; + if (*ep == '\0') + break; + + if (!ch_handled) + *swallowed_p++ = ch; + ch_handled = 1; + } else + ep = exitmsg; + if (!ch_handled) { + char *p; + /* Print out any characters which have been swallowed. */ + for (p = swallowed; p < swallowed_p; ++p) + putc (*p, stdout); + swallowed_p = swallowed; + putc (ch, stdout); + } + } + expect_prompt (); + if (*bp== '\0') + { + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + } + else + { + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = 0; + } + timeout = old_timeout; + immediate_quit = old_immediate_quit; + return 0; +} + +/* Return the name of register number REGNO + in the form input and output by adapt. + + Returns a pointer to a static buffer containing the answer. */ +static char * +get_reg_name (regno) + int regno; +{ + static char buf[80]; + if (regno >= GR96_REGNUM && regno < GR96_REGNUM + 32 ) + sprintf (buf, "GR%03d", regno - GR96_REGNUM + 96); +#if defined(GR64_REGNUM) + else if (regno >= GR64_REGNUM && regno < GR64_REGNUM + 32 ) + sprintf (buf, "GR%03d", regno - GR64_REGNUM + 64); +#endif + else if (regno >= LR0_REGNUM && regno < LR0_REGNUM + 128) + sprintf (buf, "LR%03d", regno - LR0_REGNUM); + else if (regno == Q_REGNUM) + strcpy (buf, "SR131"); + else if (regno >= BP_REGNUM && regno <= CR_REGNUM) + sprintf (buf, "SR%03d", regno - BP_REGNUM + 133); + else if (regno == ALU_REGNUM) + strcpy (buf, "SR132"); + else if (regno >= IPC_REGNUM && regno <= IPB_REGNUM) + sprintf (buf, "SR%03d", regno - IPC_REGNUM + 128); + else if (regno >= VAB_REGNUM && regno <= LRU_REGNUM) { + /* When a 29050 is in freeze-mode, read shadow pcs instead */ + if ((regno >= NPC_REGNUM && regno <= PC2_REGNUM) && USE_SHADOW_PC) + sprintf (buf, "SR%03d", regno - NPC_REGNUM + 20); + else + sprintf (buf, "SR%03d", regno - VAB_REGNUM); + } + else if (regno == GR1_REGNUM) + strcpy (buf, "GR001"); + return buf; +} + +/* Read the remote registers. */ + +static void +adapt_fetch_registers () +{ + int reg_index; + int regnum_index; + char tempbuf[10]; + int sreg_buf[16]; + int i,j; + +/* + * Global registers + */ +#if defined(GR64_REGNUM) + write (adapt_desc, "dw gr64,gr95\r", 13); + for (reg_index = 64, regnum_index = GR64_REGNUM; + reg_index < 96; + reg_index += 4, regnum_index += 4) + { + sprintf (tempbuf, "GR%03d ", reg_index); + expect (tempbuf); + get_hex_regs (4, regnum_index); + expect ("\n"); + } +#endif + write (adapt_desc, "dw gr96,gr127\r", 14); + for (reg_index = 96, regnum_index = GR96_REGNUM; + reg_index < 128; + reg_index += 4, regnum_index += 4) + { + sprintf (tempbuf, "GR%03d ", reg_index); + expect (tempbuf); + get_hex_regs (4, regnum_index); + expect ("\n"); + } + +/* + * Local registers + */ + for (i = 0; i < 128; i += 32) + { + /* The PC has a tendency to hang if we get these + all in one fell swoop ("dw lr0,lr127"). */ + sprintf (tempbuf, "dw lr%d\r", i); + write (adapt_desc, tempbuf, strlen (tempbuf)); + for (reg_index = i, regnum_index = LR0_REGNUM + i; + reg_index < i + 32; + reg_index += 4, regnum_index += 4) + { + sprintf (tempbuf, "LR%03d ", reg_index); + expect (tempbuf); + get_hex_regs (4, regnum_index); + expect ("\n"); + } + } + +/* + * Special registers + */ + sprintf (tempbuf, "dw sr0\r"); + write (adapt_desc, tempbuf, strlen (tempbuf)); + for (i=0 ; i<4 ; i++) { /* SR0 - SR14 */ + sprintf (tempbuf, "SR%3d",i*4); + expect(tempbuf); + for (j=0 ; j < (i==3 ? 3 : 4) ; j++) + sreg_buf[i*4 + j] = get_hex_word(); + } + expect_prompt(); + /* + * Read the pcs individually if we are in freeze mode. + * See get_reg_name(), it translates the register names for the pcs to + * the names of the shadow pcs. + */ + if (USE_SHADOW_PC) { + sreg_buf[10] = read_register(NPC_REGNUM); /* pc0 */ + sreg_buf[11] = read_register(PC_REGNUM); /* pc1 */ + sreg_buf[12] = read_register(PC2_REGNUM); /* pc2 */ + } + for (i=0 ; i<14 ; i++) /* Supply vab -> lru */ + supply_register(VAB_REGNUM+i, (char *) &sreg_buf[i]); + sprintf (tempbuf, "dw sr128\r"); + write (adapt_desc, tempbuf, strlen (tempbuf)); + for (i=0 ; i<2 ; i++) { /* SR128 - SR135 */ + sprintf (tempbuf, "SR%3d",128 + i*4); + expect(tempbuf); + for (j=0 ; j<4 ; j++) + sreg_buf[i*4 + j] = get_hex_word(); + } + expect_prompt(); + supply_register(IPC_REGNUM,(char *) &sreg_buf[0]); + supply_register(IPA_REGNUM,(char *) &sreg_buf[1]); + supply_register(IPB_REGNUM,(char *) &sreg_buf[2]); + supply_register(Q_REGNUM, (char *) &sreg_buf[3]); + /* Skip ALU */ + supply_register(BP_REGNUM, (char *) &sreg_buf[5]); + supply_register(FC_REGNUM, (char *) &sreg_buf[6]); + supply_register(CR_REGNUM, (char *) &sreg_buf[7]); + + /* There doesn't seem to be any way to get these. */ + { + int val = -1; + supply_register (FPE_REGNUM, (char *) &val); + supply_register (INTE_REGNUM, (char *) &val); + supply_register (FPS_REGNUM, (char *) &val); + supply_register (EXO_REGNUM, (char *) &val); + } + + write (adapt_desc, "dw gr1,gr1\r", 11); + expect ("GR001 "); + get_hex_regs (1, GR1_REGNUM); + expect_prompt (); +} + +/* Fetch register REGNO, or all registers if REGNO is -1. + */ +static void +adapt_fetch_register (regno) + int regno; +{ + if (regno == -1) + adapt_fetch_registers (); + else + { + char *name = get_reg_name (regno); + fprintf (adapt_stream, "dw %s,%s\r", name, name); + expect (name); + expect (" "); + get_hex_regs (1, regno); + expect_prompt (); + } +} + +/* Store the remote registers from the contents of the block REGS. */ + +static void +adapt_store_registers () +{ + int i, j; + + fprintf (adapt_stream, "s gr1,%x\r", read_register (GR1_REGNUM)); + expect_prompt (); + +#if defined(GR64_REGNUM) + for (j = 0; j < 32; j += 16) + { + fprintf (adapt_stream, "s gr%d,", j + 64); + for (i = 0; i < 15; ++i) + fprintf (adapt_stream, "%x,", read_register (GR64_REGNUM + j + i)); + fprintf (adapt_stream, "%x\r", read_register (GR64_REGNUM + j + 15)); + expect_prompt (); + } +#endif + for (j = 0; j < 32; j += 16) + { + fprintf (adapt_stream, "s gr%d,", j + 96); + for (i = 0; i < 15; ++i) + fprintf (adapt_stream, "%x,", read_register (GR96_REGNUM + j + i)); + fprintf (adapt_stream, "%x\r", read_register (GR96_REGNUM + j + 15)); + expect_prompt (); + } + + for (j = 0; j < 128; j += 16) + { + fprintf (adapt_stream, "s lr%d,", j); + for (i = 0; i < 15; ++i) + fprintf (adapt_stream, "%x,", read_register (LR0_REGNUM + j + i)); + fprintf (adapt_stream, "%x\r", read_register (LR0_REGNUM + j + 15)); + expect_prompt (); + } + + fprintf (adapt_stream, "s sr128,%x,%x,%x\r", read_register (IPC_REGNUM), + read_register (IPA_REGNUM), read_register (IPB_REGNUM)); + expect_prompt (); + fprintf (adapt_stream, "s sr133,%x,%x,%x\r", read_register (BP_REGNUM), + read_register (FC_REGNUM), read_register (CR_REGNUM)); + expect_prompt (); + fprintf (adapt_stream, "s sr131,%x\r", read_register (Q_REGNUM)); + expect_prompt (); + fprintf (adapt_stream, "s sr0,"); + for (i=0 ; i<7 ; ++i) + fprintf (adapt_stream, "%x,", read_register (VAB_REGNUM + i)); + expect_prompt (); + fprintf (adapt_stream, "s sr7,"); + for (i=7; i<14 ; ++i) + fprintf (adapt_stream, "%x,", read_register (VAB_REGNUM + i)); + expect_prompt (); +} + +/* Store register REGNO, or all if REGNO == -1. + Return errno value. */ +void +adapt_store_register (regno) + int regno; +{ + /* printf("adapt_store_register() called.\n"); fflush(stdout); /* */ + if (regno == -1) + adapt_store_registers (); + else + { + char *name = get_reg_name (regno); + fprintf (adapt_stream, "s %s,%x\r", name, read_register (regno)); + /* Setting GR1 changes the numbers of all the locals, so + invalidate the register cache. Do this *after* calling + read_register, because we want read_register to return the + value that write_register has just stuffed into the registers + array, not the value of the register fetched from the + inferior. */ + if (regno == GR1_REGNUM) + registers_changed (); + expect_prompt (); + } +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +void +adapt_prepare_to_store () +{ + /* Do nothing, since we can store individual regs */ +} + +static CORE_ADDR +translate_addr(addr) +CORE_ADDR addr; +{ +#if defined(KERNEL_DEBUGGING) + /* Check for a virtual address in the kernel */ + /* Assume physical address of ublock is in paddr_u register */ + if (addr >= UVADDR) { + /* PADDR_U register holds the physical address of the ublock */ + CORE_ADDR i = (CORE_ADDR)read_register(PADDR_U_REGNUM); + return(i + addr - (CORE_ADDR)UVADDR); + } else { + return(addr); + } +#else + return(addr); +#endif +} + + +/* FIXME! Merge these two. */ +int +adapt_xfer_inferior_memory (memaddr, myaddr, len, write) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; +{ + + memaddr = translate_addr(memaddr); + + if (write) + return adapt_write_inferior_memory (memaddr, myaddr, len); + else + return adapt_read_inferior_memory (memaddr, myaddr, len); +} + +void +adapt_files_info () +{ + printf_filtered("\tAttached to %s at %d baud and running program %s\n", + dev_name, baudrate, prog_name); + printf_filtered("\ton an %s processor.\n", processor_name[processor_type]); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. Returns errno value. + * sb/sh instructions don't work on unaligned addresses, when TU=1. + */ +int +adapt_write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + int i; + unsigned int cps; + + /* Turn TU bit off so we can do 'sb' commands */ + cps = read_register(CPS_REGNUM); + if (cps & 0x00000800) + write_register(CPS_REGNUM,cps&~(0x00000800)); + + for (i = 0; i < len; i++) + { + if ((i % 16) == 0) + fprintf (adapt_stream, "sb %x,", memaddr + i); + if ((i % 16) == 15 || i == len - 1) + { + fprintf (adapt_stream, "%x\r", ((unsigned char *)myaddr)[i]); + expect_prompt (); + } + else + fprintf (adapt_stream, "%x,", ((unsigned char *)myaddr)[i]); + } + /* Restore the old value of cps if the TU bit was on */ + if (cps & 0x00000800) + write_register(CPS_REGNUM,cps); + return len; +} + +/* Read LEN bytes from inferior memory at MEMADDR. Put the result + at debugger address MYADDR. Returns errno value. */ +int +adapt_read_inferior_memory(memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + int i; + + /* Number of bytes read so far. */ + int count; + + /* Starting address of this pass. */ + unsigned long startaddr; + + /* Number of bytes to read in this pass. */ + int len_this_pass; + + /* Note that this code works correctly if startaddr is just less + than UINT_MAX (well, really CORE_ADDR_MAX if there was such a + thing). That is, something like + adapt_read_bytes (CORE_ADDR_MAX - 4, foo, 4) + works--it never adds len to memaddr and gets 0. */ + /* However, something like + adapt_read_bytes (CORE_ADDR_MAX - 3, foo, 4) + doesn't need to work. Detect it and give up if there's an attempt + to do that. */ + + if (((memaddr - 1) + len) < memaddr) + return EIO; + + startaddr = memaddr; + count = 0; + while (count < len) + { + len_this_pass = 16; + if ((startaddr % 16) != 0) + len_this_pass -= startaddr % 16; + if (len_this_pass > (len - count)) + len_this_pass = (len - count); + + fprintf (adapt_stream, "db %x,%x\r", startaddr, + (startaddr - 1) + len_this_pass); + +#ifdef NOTDEF /* Why do this */ + expect ("\n"); + /* Look for 8 hex digits. */ + i = 0; + while (1) + { + if (isxdigit (readchar ())) + ++i; + else + { + expect_prompt (); + error ("Hex digit expected from remote system."); + } + if (i >= 8) + break; + } +#endif /* NOTDEF */ + + expect (" "); + + for (i = 0; i < len_this_pass; i++) + get_hex_byte (&myaddr[count++]); + + expect_prompt (); + + startaddr += len_this_pass; + } + return count; +} + +#define MAX_BREAKS 8 +static int num_brkpts=0; +static int +adapt_insert_breakpoint(addr, save) +CORE_ADDR addr; +char *save; /* Throw away, let adapt save instructions */ +{ + if (num_brkpts < MAX_BREAKS) { + num_brkpts++; + fprintf (adapt_stream, "B %x", addr); + fprintf (adapt_stream, "\r"); + expect_prompt (); + return(0); /* Success */ + } else { + fprintf_filtered(gdb_stderr, + "Too many break points, break point not installed\n"); + return(1); /* Failure */ + } + +} +static int +adapt_remove_breakpoint(addr, save) +CORE_ADDR addr; +char *save; /* Throw away, let adapt save instructions */ +{ + if (num_brkpts > 0) { + num_brkpts--; + fprintf (adapt_stream, "BR %x", addr); + fprintf (adapt_stream, "\r"); + fflush (adapt_stream); + expect_prompt (); + } + return(0); +} + +/* Clear the adapts notion of what the break points are */ +static int +adapt_clear_breakpoints() +{ + if (adapt_stream) { + fprintf (adapt_stream, "BR"); /* Clear all break points */ + fprintf (adapt_stream, "\r"); + fflush(adapt_stream); + expect_prompt (); + } + num_brkpts = 0; +} +static void +adapt_mourn() +{ + adapt_clear_breakpoints(); + pop_target (); /* Pop back to no-child state */ + generic_mourn_inferior (); +} + +/* Display everthing we read in from the adapt until we match/see the + * specified string + */ +static int +display_until(str) +char *str; +{ + int i=0,j,c; + + while (c=readchar()) { + if (c==str[i]) { + i++; + if (i == strlen(str)) return; + } else { + if (i) { + for (j=0 ; j<i ; j++) /* Put everthing we matched */ + putchar(str[j]); + i=0; + } + putchar(c); + } + } + +} + + +/* Put a command string, in args, out to the adapt. The adapt is assumed to + be in raw mode, all writing/reading done through adapt_desc. + Ouput from the adapt is placed on the users terminal until the + prompt from the adapt is seen. + FIXME: Can't handle commands that take input. */ + +void +adapt_com (args, fromtty) + char *args; + int fromtty; +{ + if (!adapt_stream) { + printf_filtered("Adapt not open. Use the 'target' command to open.\n"); + return; + } + + /* Clear all input so only command relative output is displayed */ + slurp_input(); + + switch(islower(args[0]) ? toupper(args[0]) : args[0]) { + default: + printf_filtered("Unknown/Unimplemented adapt command '%s'\n",args); + break; + case 'G': /* Go, begin execution */ + write(adapt_desc,args,strlen(args)); + write(adapt_desc,"\r",1); + expect_prompt(); + break; + case 'B': /* Break points, B or BR */ + case 'C': /* Check current 29k status (running/halted) */ + case 'D': /* Display data/registers */ + case 'I': /* Input from i/o space */ + case 'J': /* Jam an instruction */ + case 'K': /* Kill, stop execution */ + case 'L': /* Disassemble */ + case 'O': /* Output to i/o space */ + case 'T': /* Trace */ + case 'P': /* Pulse an input line */ + case 'X': /* Examine special purpose registers */ + case 'Z': /* Display trace buffer */ + write(adapt_desc,args,strlen(args)); + write(adapt_desc,"\r",1); + expect(args); /* Don't display the command */ + display_until("# "); + break; + /* Begin commands that take input in the form 'c x,y[,z...]' */ + case 'S': /* Set memory or register */ + if (strchr(args,',')) { /* Assume it is properly formatted */ + write(adapt_desc,args,strlen(args)); + write(adapt_desc,"\r",1); + expect_prompt(); + } + break; + } +} + +/* Define the target subroutine names */ + +struct target_ops adapt_ops ; + +static void +init_adapt_ops(void) +{ + adapt_ops.to_shortname = "adapt"; + adapt_ops.to_longname = "Remote AMD `Adapt' target"; + adapt_ops.to_doc = "Remote debug an AMD 290*0 using an `Adapt' monitor via RS232"; + adapt_ops.to_open = adapt_open; + adapt_ops.to_close = adapt_close; + adapt_ops.to_attach = adapt_attach; + adapt_ops.to_post_attach = NULL; + adapt_ops.to_require_attach = NULL; + adapt_ops.to_detach = adapt_detach; + adapt_ops.to_require_detach = NULL; + adapt_ops.to_resume = adapt_resume; + adapt_ops.to_wait = adapt_wait; + adapt_ops.to_post_wait = NULL; + adapt_ops.to_fetch_registers = adapt_fetch_register; + adapt_ops.to_store_registers = adapt_store_register; + adapt_ops.to_prepare_to_store = adapt_prepare_to_store; + adapt_ops.to_xfer_memory = adapt_xfer_inferior_memory; + adapt_ops.to_files_info = adapt_files_info; + adapt_ops.to_insert_breakpoint = adapt_insert_breakpoint; + adapt_ops.to_remove_breakpoint = adapt_remove_breakpoint; + adapt_ops.to_terminal_init = 0; + adapt_ops.to_terminal_inferior = 0; + adapt_ops.to_terminal_ours_for_output = 0; + adapt_ops.to_terminal_ours = 0; + adapt_ops.to_terminal_info = 0; + adapt_ops.to_kill = adapt_kill; + adapt_ops.to_load = adapt_load; + adapt_ops.to_lookup_symbol = 0; + adapt_ops.to_create_inferior = adapt_create_inferior; + adapt_ops.to_post_startup_inferior = NULL; + adapt_ops.to_acknowledge_created_inferior = NULL; + adapt_ops.to_clone_and_follow_inferior = NULL; + adapt_ops.to_post_follow_inferior_by_clone = NULL; + adapt_ops.to_insert_fork_catchpoint = NULL; + adapt_ops.to_remove_fork_catchpoint = NULL; + adapt_ops.to_insert_vfork_catchpoint = NULL; + adapt_ops.to_remove_vfork_catchpoint = NULL; + adapt_ops.to_has_forked = NULL; + adapt_ops.to_has_vforked = NULL; + adapt_ops.to_can_follow_vfork_prior_to_exec = NULL; + adapt_ops.to_post_follow_vfork = NULL; + adapt_ops.to_insert_exec_catchpoint = NULL; + adapt_ops.to_remove_exec_catchpoint = NULL; + adapt_ops.to_has_execd = NULL; + adapt_ops.to_reported_exec_events_per_exec_call = NULL; + adapt_ops.to_has_exited = NULL; + adapt_ops.to_mourn_inferior = adapt_mourn; + adapt_ops.to_can_run = 0; + adapt_ops.to_notice_signals = 0; + adapt_ops.to_thread_alive = 0; + adapt_ops.to_stop = 0 ; /* process_stratum; */ + adapt_ops.to_pid_to_exec_file = NULL; + adapt_ops.to_core_file_to_sym_file = NULL; + adapt_ops.to_stratum = 0; + adapt_ops.DONT_USE = 0 ; + adapt_ops.to_has_all_memory = 1; + adapt_ops.to_has_memory = 1; + adapt_ops.to_has_stack = 1; + adapt_ops.to_has_registers = 1; + adapt_ops.to_has_execution = 0; + adapt_ops.to_sections = 0; + adapt_ops.to_sections_end = 0 ; + adapt_ops.to_magic = OPS_MAGIC; +} /* init_adapt_ops */ + +void +_initialize_remote_adapt () +{ + init_adapt_ops() ; + add_target (&adapt_ops); + add_com ("adapt <command>", class_obscure, adapt_com, + "Send a command to the AMD Adapt remote monitor."); +} |