diff options
Diffstat (limited to 'gdb/remote-bug.c')
-rw-r--r-- | gdb/remote-bug.c | 1485 |
1 files changed, 1485 insertions, 0 deletions
diff --git a/gdb/remote-bug.c b/gdb/remote-bug.c new file mode 100644 index 0000000..43c4cd3 --- /dev/null +++ b/gdb/remote-bug.c @@ -0,0 +1,1485 @@ +/* Remote debugging interface for Motorola's MVME187BUG monitor, an embedded + monitor for the m88k. + + Copyright 1992, 1993 Free Software Foundation, Inc. + Contributed by Cygnus Support. Written by K. Richard Pixley. + +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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "defs.h" +#include "inferior.h" +#include "wait.h" +#include "value.h" +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <signal.h> +#include <setjmp.h> +#include <errno.h> +#include "terminal.h" +#include "target.h" +#include "gdbcore.h" +#include "serial.h" + +/* External data declarations */ +extern int stop_soon_quietly; /* for wait_for_inferior */ + +/* Forward data declarations */ +static struct target_ops bug_ops; /* Forward declaration */ + +/* Forward function declarations */ +static void bug_fetch_registers (); +static int bug_store_registers (); +static void bug_close (); +static int bug_clear_breakpoints (); + +static int quiet = 1; + + +serial_t desc; + +/***********************************************************************/ +/* Caching stuff stolen from remote-nindy.c */ + +/* The data cache records all the data read from the remote machine + since the last time it stopped. + + Each cache block holds LINE_SIZE bytes of data + starting at a multiple-of-LINE_SIZE address. */ + +#define LINE_SIZE_POWER 4 +#define LINE_SIZE (1<<LINE_SIZE_POWER) /* eg 1<<3 == 8 */ +#define LINE_SIZE_MASK ((LINE_SIZE-1)) /* eg 7*2+1= 111*/ +#define DCACHE_SIZE 64 /* Number of cache blocks */ +#define XFORM(x) ((x&LINE_SIZE_MASK)>>2) +struct dcache_block + { + struct dcache_block *next, *last; + unsigned int addr; /* Address for which data is recorded. */ + int data[LINE_SIZE / sizeof (int)]; + }; + +struct dcache_block dcache_free, dcache_valid; + +/* Free all the data cache blocks, thus discarding all cached data. */ +static +void +dcache_flush () +{ + register struct dcache_block *db; + + while ((db = dcache_valid.next) != &dcache_valid) + { + remque (db); + insque (db, &dcache_free); + } +} + +/* + * If addr is present in the dcache, return the address of the block + * containing it. + */ +static +struct dcache_block * +dcache_hit (addr) + unsigned int addr; +{ + register struct dcache_block *db; + + if (addr & 3) + abort (); + + /* Search all cache blocks for one that is at this address. */ + db = dcache_valid.next; + while (db != &dcache_valid) + { + if ((addr & ~LINE_SIZE_MASK) == db->addr) + return db; + db = db->next; + } + return NULL; +} + +/* Return the int data at address ADDR in dcache block DC. */ +static +int +dcache_value (db, addr) + struct dcache_block *db; + unsigned int addr; +{ + if (addr & 3) + abort (); + return (db->data[XFORM (addr)]); +} + +/* Get a free cache block, put or keep it on the valid list, + and return its address. The caller should store into the block + the address and data that it describes, then remque it from the + free list and insert it into the valid list. This procedure + prevents errors from creeping in if a ninMemGet is interrupted + (which used to put garbage blocks in the valid list...). */ +static +struct dcache_block * +dcache_alloc () +{ + register struct dcache_block *db; + + if ((db = dcache_free.next) == &dcache_free) + { + /* If we can't get one from the free list, take last valid and put + it on the free list. */ + db = dcache_valid.last; + remque (db); + insque (db, &dcache_free); + } + + remque (db); + insque (db, &dcache_valid); + return (db); +} + +/* Return the contents of the word at address ADDR in the remote machine, + using the data cache. */ +static +int +dcache_fetch (addr) + CORE_ADDR addr; +{ + register struct dcache_block *db; + + db = dcache_hit (addr); + if (db == 0) + { + db = dcache_alloc (); + immediate_quit++; + bug_read_inferior_memory (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE); + immediate_quit--; + db->addr = addr & ~LINE_SIZE_MASK; + remque (db); /* Off the free list */ + insque (db, &dcache_valid); /* On the valid list */ + } + return (dcache_value (db, addr)); +} + +/* Write the word at ADDR both in the data cache and in the remote machine. */ +static void +dcache_poke (addr, data) + CORE_ADDR addr; + int data; +{ + register struct dcache_block *db; + + /* First make sure the word is IN the cache. DB is its cache block. */ + db = dcache_hit (addr); + if (db == 0) + { + db = dcache_alloc (); + immediate_quit++; + bug_write_inferior_memory (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE); + immediate_quit--; + db->addr = addr & ~LINE_SIZE_MASK; + remque (db); /* Off the free list */ + insque (db, &dcache_valid); /* On the valid list */ + } + + /* Modify the word in the cache. */ + db->data[XFORM (addr)] = data; + + /* Send the changed word. */ + immediate_quit++; + bug_write_inferior_memory (addr, (unsigned char *) &data, 4); + immediate_quit--; +} + +/* The cache itself. */ +struct dcache_block the_cache[DCACHE_SIZE]; + +/* Initialize the data cache. */ +static void +dcache_init () +{ + register i; + register struct dcache_block *db; + + db = the_cache; + dcache_free.next = dcache_free.last = &dcache_free; + dcache_valid.next = dcache_valid.last = &dcache_valid; + for (i = 0; i < DCACHE_SIZE; i++, db++) + insque (db, &dcache_free); +} + +/*********************************************************************** + * I/O stuff stolen from remote-eb.c + ***********************************************************************/ + +static int timeout = 2; + +static const char *dev_name; + +/* Descriptor for I/O to remote machine. Initialize it to -1 so that + bug_open knows that we don't have a file open when the program + starts. */ + +int is_open = 0; +int +check_open () +{ + if (!is_open) + { + error ("remote device not open"); + } +} + +#define ON 1 +#define OFF 0 + +/* Read a character from the remote system, doing all the fancy + timeout stuff. */ +static int +readchar () +{ + int buf; + + buf = SERIAL_READCHAR (desc, timeout); + + if (buf == SERIAL_TIMEOUT) + error ("Timeout reading from remote system."); + + if (!quiet) + printf ("%c", buf); + + return buf & 0x7f; +} + +static int +readchar_nofail () +{ + int buf; + + buf = SERIAL_READCHAR (desc, timeout); + if (buf == SERIAL_TIMEOUT) + buf = 0; + if (!quiet) + printf ("%c", buf); + + 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; + + 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 bug 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: bug_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 bug_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 () +{ + expect ("Bug>"); +} + +/* 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 bug_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 bug, 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; +} + +/* Called when SIGALRM signal sent due to alarm() timeout. */ + +/* Number of SIGTRAPs we need to simulate. That is, the next + NEED_ARTIFICIAL_TRAP calls to bug_wait should just return + SIGTRAP without actually waiting for anything. */ + +static int need_artificial_trap = 0; + +void +bug_kill (arg, from_tty) + char *arg; + int from_tty; +{ + +} + +/* + * Download a file specified in 'args', to the bug. + */ +static void +bug_load (args, fromtty) + char *args; + int fromtty; +{ + bfd *abfd; + asection *s; + int n; + char buffer[1024]; + + check_open (); + + dcache_flush (); + inferior_pid = 0; + abfd = bfd_openr (args, 0); + if (!abfd) + { + printf_filtered ("Unable to open file %s\n", args); + return; + } + + if (bfd_check_format (abfd, bfd_object) == 0) + { + printf_filtered ("File is not an object file\n"); + return; + } + + s = abfd->sections; + while (s != (asection *) NULL) + { + if (s->flags & SEC_LOAD) + { + int i; + +#define DELTA (1024) + char *buffer = xmalloc (DELTA); + + printf_filtered ("%s\t: 0x%4x .. 0x%4x ", s->name, s->vma, s->vma + s->_raw_size); + for (i = 0; i < s->_raw_size; i += DELTA) + { + int delta = DELTA; + + if (delta > s->_raw_size - i) + delta = s->_raw_size - i; + + bfd_get_section_contents (abfd, s, buffer, i, delta); + bug_write_inferior_memory (s->vma + i, buffer, delta); + printf_filtered ("*"); + fflush (stdout); + } + printf_filtered ("\n"); + free (buffer); + } + s = s->next; + } + sprintf (buffer, "rs ip %x", abfd->start_address); + bug_write_cr (buffer); + expect_prompt (); +} + +/* This is called not only when we first attach, but also when the + user types "run" after having attached. */ +void +bug_create_inferior (execfile, args, env) + char *execfile; + char *args; + char **env; +{ + int entry_pt; + char buffer[100]; + + if (args && *args) + error ("Can't pass arguments to remote bug process."); + + if (execfile == 0 || exec_bfd == 0) + error ("No exec file specified"); + + entry_pt = (int) bfd_get_start_address (exec_bfd); + check_open (); + + bug_kill (NULL, NULL); + bug_clear_breakpoints (); + init_wait_for_inferior (); + bug_write_cr (""); + expect_prompt (); + + insert_breakpoints (); /* Needed to get correct instruction in cache */ + proceed (entry_pt, -1, 0); +} + +/* Open a connection to a remote debugger. + NAME is the filename used for communication, then a space, + then the baud rate. + */ + +static char * +find_end_of_word (s) + char *s; +{ + while (*s && !isspace (*s)) + s++; + return s; +} + +static char * +get_word (p) + char **p; +{ + char *s = *p; + char *word; + char *copy; + size_t len; + + while (isspace (*s)) + s++; + + word = s; + + len = 0; + + while (*s && !isspace (*s)) + { + s++; + len++; + + } + copy = xmalloc (len + 1); + memcpy (copy, word, len); + copy[len] = 0; + *p = s; + return copy; +} + +static int baudrate = 9600; + +#if 0 +static int +is_baudrate_right () +{ + int ok; + + /* Put this port into NORMAL mode, send the 'normal' character */ + + bug_write ("\001", 1); /* Control A */ + bug_write ("\r", 1); /* Cr */ + + while (1) + { + ok = SERIAL_READCHAR (desc, timeout); + if (ok < 0) + break; + } + + bug_write ("r", 1); + + if (readchar_nofail () == 'r') + return 1; + + /* Not the right baudrate, or the board's not on */ + return 0; +} +#endif /* not */ + +static void +set_rate () +{ + if (!SERIAL_SETBAUDRATE (desc, baudrate)) + error ("Can't set baudrate"); +} + + +static void +bug_open (name, from_tty) + char *name; + int from_tty; +{ + unsigned int prl; + char *p; + + push_target (&bug_ops); + + if (name == 0) + { + name = ""; + } + if (is_open) + bug_close (0); + dev_name = strdup (name); + + if (!(desc = SERIAL_OPEN (dev_name))) + perror_with_name ((char *) dev_name); + + SERIAL_RAW (desc); + is_open = 1; + + dcache_init (); + + /* Hello? Are you there? */ + SERIAL_WRITE (desc, "\r", 1); + expect_prompt (); + + /* Clear any break points */ + bug_clear_breakpoints (); + + printf_filtered ("Connected to remote 187bug system.\n"); +} + +/* Close out all files and local state before this target loses control. */ + +static void +bug_close (quitting) + int quitting; +{ + /* Clear any break points */ + bug_clear_breakpoints (); + + if (is_open) + SERIAL_CLOSE (desc); + + is_open = 0; +} + +/* Terminate the open connection to the remote debugger. + Use this when you want to detach and do something else + with your gdb. */ +void +bug_detach (args, from_tty) + char *args; + int from_tty; +{ + if (is_open) + bug_clear_breakpoints (); + + pop_target (); /* calls bug_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 +bug_resume (step, sig) + int step, sig; +{ + dcache_flush (); + + if (step) + { + bug_write_cr("t"); + + /* Force the next bug_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 + bug_write_cr ("g"); + + return; +} + +/* Wait until the remote machine stops, then return, + storing status in STATUS just as `wait' would. */ + +int +not_bug_wait (status) + WAITTYPE *status; +{ + int old_timeout; + int old_quit; + int i; + + expect("Effective address: "); + i = get_hex_word(); + expect("\r"); + + WSETEXIT ((*status), 0); + + old_timeout = timeout; + timeout = 99999; /* while user program runs. */ + old_quit = immediate_quit; + immediate_quit = 1; /* helps ability to quit. */ + + while (strchr("\n\r", i = readchar()) != NULL) ;; + + immediate_quit = old_quit; + timeout = old_timeout; + + if (i == 'A') + { +/* At Breakpoint */ + expect("t Breakpoint"); + WSETSTOP ((*status), SIGTRAP); + + } + else + { +/* finished cleanly */ + ; + } + + expect_prompt(); +} + +int +bug_wait (status) + WAITTYPE *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[] = "At Breakpoint"; + static char exitmsg[] = "????-Bug>"; + 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; + int swallowed_cr = 0; + + WSETEXIT ((*status), 0); + + if (need_artificial_trap != 0) + { + WSETSTOP ((*status), SIGTRAP); + need_artificial_trap--; + /* user output from the target can be discarded here. (?) */ + expect_prompt(); + return 0; + } + + /* read off leftovers from resume */ + expect("Effective address: "); + (void) get_hex_word(); + while (strchr("\r\n", ch = readchar()) != NULL) ;; + + timeout = 99999; /* 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; + 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; + + if ((ch != '\r' && ch != '\n') || swallowed_cr > 10) + { + putc (ch, stdout); + swallowed_cr = 10; + } + swallowed_cr++; + + } + + ch = readchar (); + } + if (*bp == '\0') + { + WSETSTOP ((*status), SIGTRAP); + expect_prompt (); + } + else + { + WSETEXIT ((*status), 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 bug. + + Returns a pointer to a static buffer containing the answer. */ +static char * +get_reg_name (regno) + int regno; +{ + static char *rn[] = { + "r00", "r01", "r02", "r03", "r04", "r05", "r06", "r07", + "r08", "r09", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + + /* these get confusing because we omit a few and switch some ordering around. */ + + "cr01", /* 32 = psr */ + "fcr62", /* 33 = fpsr*/ + "fcr63", /* 34 = fpcr */ + "cr04", /* 35 = sxip */ + "cr05", /* 36 = snip */ + "cr06", /* 37 = sfip */ + }; + + return rn[regno]; +} + +static int +gethex (length, start, ok) + unsigned int length; + char *start; + int *ok; +{ + int result = 0; + + while (length--) + { + result <<= 4; + if (*start >= 'a' && *start <= 'f') + { + result += *start - 'a' + 10; + } + else if (*start >= 'A' && *start <= 'F') + { + result += *start - 'A' + 10; + } + else if (*start >= '0' && *start <= '9') + { + result += *start - '0'; + } + else + *ok = 0; + start++; + + } + return result; +} +static int +timed_read (buf, n, timeout) + char *buf; + +{ + int i; + char c; + + i = 0; + while (i < n) + { + c = readchar (); + + if (c == 0) + return i; + buf[i] = c; + i++; + + } + return i; +} + +bug_write (a, l) + char *a; +{ + int i; + + SERIAL_WRITE (desc, a, l); + + if (!quiet) + for (i = 0; i < l; i++) + { + printf ("%c", a[i]); + } +} + +bug_write_cr (s) + char *s; +{ + bug_write (s, strlen (s)); + bug_write ("\r", 1); +} + +/* Store register REGNO, or all if REGNO == -1. */ + +static void +bug_fetch_register(regno) + int regno; +{ + REGISTER_TYPE regval; + check_open(); + + if (regno == -1) + { + int i; + + for (i = 0; i < NUM_REGS; ++i) + bug_fetch_register(i); + } + else + { + bug_write("rs ", 3); + bug_write_cr(get_reg_name(regno)); + expect("="); + regval = get_hex_word(); + expect_prompt(); + + /* the following registers contain flag bits in the lower to bit slots. + Mask them off */ + if (regno == PC_REGNUM /* aka sxip */ + || regno == NPC_REGNUM /* aka snip */ + || regno == SFIP_REGNUM) /* aka sfip */ + regval &= ~0x3; + + supply_register(regno, (char *) ®val); + } + + return; +} + +/* Store register REGNO, or all if REGNO == -1. */ + +static void +bug_store_register (regno) + int regno; +{ + REGISTER_TYPE regval; + char buffer[1024]; + check_open(); + + if (regno == -1) + { + int i; + + for (i = 0; i < NUM_REGS; ++i) + bug_store_register(i); + } + else + { + char *regname; + + /* get_reg_name thinks that the pc is in sxip. This is only partially + true. When *assigning* it, we must assign to ip on m88k-bug. */ + + regname = ((regno == PC_REGNUM) + ? "ip" + : get_reg_name(regno)); + + sprintf(buffer, "rs %s %08x", + regname, + read_register(regno)); + + bug_write_cr(buffer); + expect_prompt(); + } + + return; +} + +/* 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 +bug_prepare_to_store () +{ + /* Do nothing, since we can store individual regs */ +} + +static CORE_ADDR +translate_addr (addr) + CORE_ADDR addr; +{ + + return (addr); + +} + +/* Read a word from remote address ADDR and return it. + * This goes through the data cache. + */ +int +bug_fetch_word (addr) + CORE_ADDR addr; +{ + return dcache_fetch (addr); +} + +/* Write a word WORD into remote address ADDR. + This goes through the data cache. */ + +void +bug_store_word (addr, word) + CORE_ADDR addr; + int word; +{ + dcache_poke (addr, word); +} + +int +bug_xfer_inferior_memory (memaddr, myaddr, len, write, target) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; + struct target_ops *target; /* ignored */ +{ + register int i; + + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr; + + /* Round ending address up; get number of longwords that makes. */ + register int count; + + /* Allocate buffer of that many longwords. */ + register int *buffer; + + addr = memaddr & -sizeof (int); + count = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + + buffer = (int *) alloca (count * sizeof (int)); + + if (write) + { + /* Fill start and end extra bytes of buffer with existing memory data. */ + + if (addr != memaddr || len < (int) sizeof (int)) + { + /* Need part of initial word -- fetch it. */ + buffer[0] = bug_fetch_word (addr); + } + + if (count > 1) /* FIXME, avoid if even boundary */ + { + buffer[count - 1] + = bug_fetch_word (addr + (count - 1) * sizeof (int)); + } + + /* Copy data to be written over corresponding part of buffer */ + + bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + bug_store_word (addr, buffer[i]); + if (errno) + { + + return 0; + } + + } + } + else + { + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + buffer[i] = bug_fetch_word (addr); + if (errno) + { + return 0; + } + QUIT; + } + + /* Copy appropriate bytes out of the buffer. */ + bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + } + + return len; +} + +/* fixme: make this user settable */ +#define CHUNK_SIZE (30) + +int +bug_write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + unsigned char *myaddr; + int len; +{ + int done; + int todo; + int checksum; + char buffer[(CHUNK_SIZE + 8) << 1]; + + done = 0; + +#define LOAD_COMMAND "lo 0" + bug_write_cr (LOAD_COMMAND); + + while (done < len) + { + int thisgo; + int idx; + char *buf = buffer; + CORE_ADDR address; + + checksum = 0; + thisgo = len - done; + if (thisgo > CHUNK_SIZE) + thisgo = CHUNK_SIZE; + + address = memaddr + done; + sprintf (buf, "S3%02X%08X", thisgo + 4 + 1, address); + buf += 12; + + checksum += (thisgo + 4 + 1 + + (address & 0xff) + + ((address >> 8) & 0xff) + + ((address >> 16) & 0xff) + + ((address >> 24) & 0xff)); + + for (idx = 0; idx < thisgo; idx++) + { + sprintf (buf, "%02X", myaddr[idx + done]); + checksum += myaddr[idx + done]; + buf += 2; + } + sprintf(buf, "%02X", ~checksum & 0xff); + bug_write_cr (buffer); + done += thisgo; + } + + bug_write_cr("S7060000000000F9"); + expect_prompt(); +} + +void +bug_files_info () +{ + char *file = "nothing"; + + if (exec_bfd) + file = bfd_get_filename (exec_bfd); + + if (exec_bfd) +#ifdef __GO32__ + printf_filtered ("\tAttached to DOS asynctsr and running program %s\n", file); +#else + printf_filtered ("\tAttached to %s at %d baud and running program %s\n", dev_name, baudrate, file); +#endif + printf_filtered ("\ton an m88k processor.\n"); +} + +/* 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. + */ + +/* Read LEN bytes from inferior memory at MEMADDR. Put the result + at debugger address MYADDR. Returns errno value. */ +int +bug_read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + char request[100]; + char *buffer; + char *p; + char type; + char size; + unsigned char c; + unsigned int inaddr; + unsigned int checksum; + + sprintf(request, "du 0 %x:&%d", memaddr, len); + bug_write_cr(request); + + p = buffer = alloca(len); + + /* scan up through the header */ + expect("S0030000FC"); + + while (p < buffer + len) + { + /* scan off any white space. */ + while (readchar() != 'S') ;; + + /* what kind of s-rec? */ + type = readchar(); + + /* scan record size */ + get_hex_byte(&size); + checksum = size; + --size; + inaddr = 0; + + switch (type) + { + case '7': + case '8': + case '9': + goto done; + + case '3': + get_hex_byte(&c); + inaddr = (inaddr << 8) + c; + checksum += c; + --size; + /* intentional fall through */ + case '2': + get_hex_byte(&c); + inaddr = (inaddr << 8) + c; + checksum += c; + --size; + /* intentional fall through */ + case '1': + get_hex_byte(&c); + inaddr = (inaddr << 8) + c; + checksum += c; + --size; + get_hex_byte(&c); + inaddr = (inaddr << 8) + c; + checksum += c; + --size; + break; + + default: + /* bonk */ + error("reading s-records."); + } + + if (inaddr < memaddr + || (memaddr + len) < (inaddr + size)) + error("srec out of memory range."); + + if (p != buffer + inaddr - memaddr) + error("srec out of sequence."); + + for (; size; --size, ++p) + { + get_hex_byte(p); + checksum += *p; + } + + get_hex_byte(&c); + if (c != (~checksum & 0xff)) + error("bad s-rec checksum"); + } + + done: + expect_prompt(); + if (p != buffer + len) + return(1); + + memcpy(myaddr, buffer, len); + return(0); +} + +/* This routine is run as a hook, just before the main command loop is + entered. If gdb is configured for the H8, but has not had its + target specified yet, this will loop prompting the user to do so. +*/ + +bug_before_main_loop () +{ + char ttyname[100]; + char *p, *p2; + extern FILE *instream; + + push_target (&bug_ops); +} + +#define MAX_BREAKS 16 +static int num_brkpts = 0; +static int +bug_insert_breakpoint (addr, save) + CORE_ADDR addr; + char *save; /* Throw away, let bug save instructions */ +{ + check_open (); + + if (num_brkpts < MAX_BREAKS) + { + char buffer[100]; + + num_brkpts++; + sprintf (buffer, "br %x", addr); + bug_write_cr (buffer); + expect_prompt (); + return(0); + } + else + { + fprintf_filtered (stderr, + "Too many break points, break point not installed\n"); + return(1); + } + +} +static int +bug_remove_breakpoint (addr, save) + CORE_ADDR addr; + char *save; /* Throw away, let bug save instructions */ +{ + if (num_brkpts > 0) + { + char buffer[100]; + + num_brkpts--; + sprintf (buffer, "nobr %x", addr); + bug_write_cr (buffer); + expect_prompt (); + + } + return (0); +} + +/* Clear the bugs notion of what the break points are */ +static int +bug_clear_breakpoints () +{ + + if (is_open) + { + bug_write_cr ("nobr"); + expect_prompt (); + } + num_brkpts = 0; +} +static void +bug_mourn () +{ + bug_clear_breakpoints (); + generic_mourn_inferior (); +} + +/* Put a command string, in args, out to the bug. The bug is assumed to + be in raw mode, all writing/reading done through desc. + Ouput from the bug is placed on the users terminal until the + prompt from the bug is seen. + FIXME: Can't handle commands that take input. */ + +void +bug_com (args, fromtty) + char *args; + int fromtty; +{ + check_open (); + + if (!args) + return; + + /* Clear all input so only command relative output is displayed */ + + bug_write_cr (args); + bug_write ("\030", 1); + expect_prompt (); +} + +bug_quiet () +{ + quiet = !quiet; + if (quiet) + printf_filtered ("Snoop disabled\n"); + else + printf_filtered ("Snoop enabled\n"); + +} + +bug_device (s) + char *s; +{ + if (s) + { + dev_name = get_word (&s); + } +} + +#if 0 +static +bug_speed (s) + char *s; +{ + check_open (); + + if (s) + { + char buffer[100]; + int newrate = atoi (s); + int which = 0; + + if (SERIAL_SETBAUDRATE (desc, newrate)) + error ("Can't use %d baud\n", newrate); + + printf_filtered ("Checking target is in sync\n"); + + printf_filtered ("Sending commands to set target to %d\n", + baudrate); + + sprintf (buffer, "tm %d. N 8 1", baudrate); + bug_write_cr (buffer); + } +} +#endif /* 0 */ + +static struct target_ops bug_ops = +{ + "bug", "Remote BUG monitor", + "Use the mvme187 board running the BUG monitor connected\n\ +by a serial line.", + + bug_open, bug_close, + 0, bug_detach, bug_resume, bug_wait, /* attach */ + bug_fetch_register, bug_store_register, + bug_prepare_to_store, + bug_xfer_inferior_memory, + bug_files_info, + bug_insert_breakpoint, bug_remove_breakpoint, /* Breakpoints */ + 0, 0, 0, 0, 0, /* Terminal handling */ + bug_kill, /* FIXME, kill */ + bug_load, + 0, /* lookup_symbol */ + bug_create_inferior, /* create_inferior */ + bug_mourn, /* mourn_inferior FIXME */ + 0, /* can_run */ + 0, /* notice_signals */ + process_stratum, 0, /* next */ + 1, 1, 1, 1, 1, /* all mem, mem, stack, regs, exec */ + 0, 0, /* Section pointers */ + OPS_MAGIC, /* Always the last thing */ +}; + +void +_initialize_remote_bug () +{ + add_target (&bug_ops); + + add_com ("bug <command>", class_obscure, bug_com, + "Send a command to the BUG monitor."); + add_com ("snoop", class_obscure, bug_quiet, + "Show what commands are going to the monitor"); + + add_com ("device", class_obscure, bug_device, + "Set the terminal line for BUG communications"); + +#if 0 + add_com ("speed", class_obscure, bug_speed, + "Set the terminal line speed for BUG communications"); +#endif /* 0 */ + + dev_name = NULL; +} |