| /* Remote debugging interface for Hitachi E7000 ICE, for GDB |
| Copyright 1993, 1994, 1996, 1997, 1998 Free Software Foundation, Inc. |
| Contributed by Cygnus Support. |
| |
| Written by Steve Chamberlain for Cygnus Support. |
| |
| 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. */ |
| |
| /* The E7000 is an in-circuit emulator for the Hitachi H8/300-H and |
| Hitachi-SH processor. It has serial port and a lan port. |
| |
| The monitor command set makes it difficult to load large ammounts of |
| data over the lan without using ftp - so try not to issue load |
| commands when communicating over ethernet; use the ftpload command. |
| |
| The monitor pauses for a second when dumping srecords to the serial |
| line too, so we use a slower per byte mechanism but without the |
| startup overhead. Even so, it's pretty slow... */ |
| |
| #include "defs.h" |
| #include "gdbcore.h" |
| #include "gdbarch.h" |
| #include "inferior.h" |
| #include "target.h" |
| #include "wait.h" |
| #include "value.h" |
| #include "command.h" |
| #include <signal.h> |
| #include "gdb_string.h" |
| #include "gdbcmd.h" |
| #include <sys/types.h> |
| #include "serial.h" |
| #include "remote-utils.h" |
| #include "symfile.h" |
| #include <time.h> |
| #include <ctype.h> |
| |
| |
| #if 1 |
| #define HARD_BREAKPOINTS /* Now handled by set option. */ |
| #define BC_BREAKPOINTS use_hard_breakpoints |
| #endif |
| |
| #define CTRLC 0x03 |
| #define ENQ 0x05 |
| #define ACK 0x06 |
| #define CTRLZ 0x1a |
| |
| extern void notice_quit PARAMS ((void)); |
| |
| extern void report_transfer_performance PARAMS ((unsigned long, |
| time_t, time_t)); |
| |
| extern char *sh_processor_type; |
| |
| /* Local function declarations. */ |
| |
| static void e7000_close PARAMS ((int)); |
| |
| static void e7000_fetch_register PARAMS ((int)); |
| |
| static void e7000_store_register PARAMS ((int)); |
| |
| static void e7000_command PARAMS ((char *, int)); |
| |
| static void e7000_login_command PARAMS ((char *, int)); |
| |
| static void e7000_ftp_command PARAMS ((char *, int)); |
| |
| static void e7000_drain_command PARAMS ((char *, int)); |
| |
| static void expect PARAMS ((char *)); |
| |
| static void expect_full_prompt PARAMS ((void)); |
| |
| static void expect_prompt PARAMS ((void)); |
| |
| static int e7000_parse_device PARAMS ((char *args, char *dev_name, |
| int baudrate)); |
| /* Variables. */ |
| |
| static serial_t e7000_desc; |
| |
| /* Allow user to chose between using hardware breakpoints or memory. */ |
| static int use_hard_breakpoints = 0; /* use sw breakpoints by default */ |
| |
| /* Nonzero if using the tcp serial driver. */ |
| |
| static int using_tcp; /* direct tcp connection to target */ |
| static int using_tcp_remote; /* indirect connection to target |
| via tcp to controller */ |
| |
| /* Nonzero if using the pc isa card. */ |
| |
| static int using_pc; |
| |
| extern struct target_ops e7000_ops; /* Forward declaration */ |
| |
| char *ENQSTRING = "\005"; |
| |
| /* Nonzero if some routine (as opposed to the user) wants echoing. |
| FIXME: Do this reentrantly with an extra parameter. */ |
| |
| static int echo; |
| |
| static int ctrl_c; |
| |
| static int timeout = 20; |
| |
| /* Send data to e7000debug. */ |
| |
| static void |
| puts_e7000debug (buf) |
| char *buf; |
| { |
| if (!e7000_desc) |
| error ("Use \"target e7000 ...\" first."); |
| |
| if (remote_debug) |
| printf_unfiltered ("Sending %s\n", buf); |
| |
| if (SERIAL_WRITE (e7000_desc, buf, strlen (buf))) |
| fprintf_unfiltered (gdb_stderr, "SERIAL_WRITE failed: %s\n", safe_strerror (errno)); |
| |
| /* And expect to see it echoed, unless using the pc interface */ |
| #if 0 |
| if (!using_pc) |
| #endif |
| expect (buf); |
| } |
| |
| static void |
| putchar_e7000 (x) |
| int x; |
| { |
| char b[1]; |
| |
| b[0] = x; |
| SERIAL_WRITE (e7000_desc, b, 1); |
| } |
| |
| static void |
| write_e7000 (s) |
| char *s; |
| { |
| SERIAL_WRITE (e7000_desc, s, strlen (s)); |
| } |
| |
| static int |
| normal (x) |
| int x; |
| { |
| if (x == '\n') |
| return '\r'; |
| return x; |
| } |
| |
| /* Read a character from the remote system, doing all the fancy timeout |
| stuff. Handles serial errors and EOF. If TIMEOUT == 0, and no chars, |
| returns -1, else returns next char. Discards chars > 127. */ |
| |
| static int |
| readchar (timeout) |
| int timeout; |
| { |
| int c; |
| |
| do |
| { |
| c = SERIAL_READCHAR (e7000_desc, timeout); |
| } |
| while (c > 127); |
| |
| if (c == SERIAL_TIMEOUT) |
| { |
| if (timeout == 0) |
| return -1; |
| echo = 0; |
| error ("Timeout reading from remote system."); |
| } |
| else if (c < 0) |
| error ("Serial communication error"); |
| |
| if (remote_debug) |
| { |
| putchar_unfiltered (c); |
| gdb_flush (gdb_stdout); |
| } |
| |
| return normal (c); |
| } |
| |
| #if 0 |
| char * |
| tl (x) |
| { |
| static char b[8][10]; |
| static int p; |
| |
| p++; |
| p &= 7; |
| if (x >= ' ') |
| { |
| b[p][0] = x; |
| b[p][1] = 0; |
| } |
| else |
| { |
| sprintf (b[p], "<%d>", x); |
| } |
| |
| return b[p]; |
| } |
| #endif |
| |
| /* Scan input from the remote system, until STRING is found. If |
| DISCARD is non-zero, then discard non-matching input, else print it |
| out. Let the user break out immediately. */ |
| |
| static void |
| expect (string) |
| char *string; |
| { |
| char *p = string; |
| int c; |
| int nl = 0; |
| |
| while (1) |
| { |
| c = readchar (timeout); |
| #if 0 |
| notice_quit (); |
| if (quit_flag == 1) |
| { |
| if (ctrl_c) |
| { |
| putchar_e7000 (CTRLC); |
| --ctrl_c; |
| } |
| else |
| { |
| quit (); |
| } |
| } |
| #endif |
| |
| if (echo) |
| { |
| if (c == '\r' || c == '\n') |
| { |
| if (!nl) |
| putchar_unfiltered ('\n'); |
| nl = 1; |
| } |
| else |
| { |
| nl = 0; |
| putchar_unfiltered (c); |
| } |
| gdb_flush (gdb_stdout); |
| } |
| if (normal (c) == normal (*p++)) |
| { |
| if (*p == '\0') |
| return; |
| } |
| else |
| { |
| p = string; |
| |
| if (normal (c) == normal (string[0])) |
| p++; |
| } |
| } |
| } |
| |
| /* Keep discarding input until we see the e7000 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: e7000_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 |
| e7000_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 (":"); |
| } |
| |
| static void |
| expect_full_prompt () |
| { |
| expect ("\r:"); |
| } |
| |
| static int |
| convert_hex_digit (ch) |
| int ch; |
| { |
| 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; |
| return -1; |
| } |
| |
| static int |
| get_hex (start) |
| int *start; |
| { |
| int value = convert_hex_digit (*start); |
| int try; |
| |
| *start = readchar (timeout); |
| while ((try = convert_hex_digit (*start)) >= 0) |
| { |
| value <<= 4; |
| value += try; |
| *start = readchar (timeout); |
| } |
| return value; |
| } |
| |
| #if 0 |
| /* Get N 32-bit 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; |
| int i; |
| |
| for (i = 0; i < n; i++) |
| { |
| int j; |
| |
| val = 0; |
| for (j = 0; j < 8; j++) |
| val = (val << 4) + get_hex_digit (j == 0); |
| supply_register (regno++, (char *) &val); |
| } |
| } |
| #endif |
| |
| /* This is called not only when we first attach, but also when the |
| user types "run" after having attached. */ |
| |
| static void |
| e7000_create_inferior (execfile, args, env) |
| char *execfile; |
| char *args; |
| char **env; |
| { |
| int entry_pt; |
| |
| if (args && *args) |
| error ("Can't pass arguments to remote E7000DEBUG process"); |
| |
| if (execfile == 0 || exec_bfd == 0) |
| error ("No executable file specified"); |
| |
| entry_pt = (int) bfd_get_start_address (exec_bfd); |
| |
| #ifdef CREATE_INFERIOR_HOOK |
| CREATE_INFERIOR_HOOK (0); /* No process-ID */ |
| #endif |
| |
| /* The "process" (board) is already stopped awaiting our commands, and |
| the program is already downloaded. We just set its PC and go. */ |
| |
| clear_proceed_status (); |
| |
| /* Tell wait_for_inferior that we've started a new process. */ |
| init_wait_for_inferior (); |
| |
| /* Set up the "saved terminal modes" of the inferior |
| based on what modes we are starting it with. */ |
| target_terminal_init (); |
| |
| /* Install inferior's terminal modes. */ |
| target_terminal_inferior (); |
| |
| /* insert_step_breakpoint (); FIXME, do we need this? */ |
| proceed ((CORE_ADDR) entry_pt, -1, 0); /* Let 'er rip... */ |
| } |
| |
| /* Open a connection to a remote debugger. NAME is the filename used |
| for communication. */ |
| |
| static int baudrate = 9600; |
| static char dev_name[100]; |
| |
| static char *machine = ""; |
| static char *user = ""; |
| static char *passwd = ""; |
| static char *dir = ""; |
| |
| /* Grab the next token and buy some space for it */ |
| |
| static char * |
| next (ptr) |
| char **ptr; |
| { |
| char *p = *ptr; |
| char *s; |
| char *r; |
| int l = 0; |
| |
| while (*p && *p == ' ') |
| p++; |
| s = p; |
| while (*p && (*p != ' ' && *p != '\t')) |
| { |
| l++; |
| p++; |
| } |
| r = xmalloc (l + 1); |
| memcpy (r, s, l); |
| r[l] = 0; |
| *ptr = p; |
| return r; |
| } |
| |
| static void |
| e7000_login_command (args, from_tty) |
| char *args; |
| int from_tty; |
| { |
| if (args) |
| { |
| machine = next (&args); |
| user = next (&args); |
| passwd = next (&args); |
| dir = next (&args); |
| if (from_tty) |
| { |
| printf_unfiltered ("Set info to %s %s %s %s\n", machine, user, passwd, dir); |
| } |
| } |
| else |
| { |
| error ("Syntax is ftplogin <machine> <user> <passwd> <directory>"); |
| } |
| } |
| |
| /* Start an ftp transfer from the E7000 to a host */ |
| |
| static void |
| e7000_ftp_command (args, from_tty) |
| char *args; |
| int from_tty; |
| { |
| /* FIXME: arbitrary limit on machine names and such. */ |
| char buf[200]; |
| |
| int oldtimeout = timeout; |
| timeout = remote_timeout; |
| |
| sprintf (buf, "ftp %s\r", machine); |
| puts_e7000debug (buf); |
| expect (" Username : "); |
| sprintf (buf, "%s\r", user); |
| puts_e7000debug (buf); |
| expect (" Password : "); |
| write_e7000 (passwd); |
| write_e7000 ("\r"); |
| expect ("success\r"); |
| expect ("FTP>"); |
| sprintf (buf, "cd %s\r", dir); |
| puts_e7000debug (buf); |
| expect ("FTP>"); |
| sprintf (buf, "ll 0;s:%s\r", args); |
| puts_e7000debug (buf); |
| expect ("FTP>"); |
| puts_e7000debug ("bye\r"); |
| expect (":"); |
| timeout = oldtimeout; |
| } |
| |
| static int |
| e7000_parse_device (args, dev_name, baudrate) |
| char *args; |
| char *dev_name; |
| int baudrate; |
| { |
| char junk[128]; |
| int n = 0; |
| if (args && strcasecmp (args, "pc") == 0) |
| { |
| strcpy (dev_name, args); |
| using_pc = 1; |
| } |
| else |
| { |
| /* FIXME! temp hack to allow use with port master - |
| target tcp_remote <device> */ |
| if (args && strncmp (args, "tcp", 10) == 0) |
| { |
| char com_type[128]; |
| n = sscanf (args, " %s %s %d %s", com_type, dev_name, &baudrate, junk); |
| using_tcp_remote = 1; |
| n--; |
| } |
| else if (args) |
| { |
| n = sscanf (args, " %s %d %s", dev_name, &baudrate, junk); |
| } |
| |
| if (n != 1 && n != 2) |
| { |
| error ("Bad arguments. Usage:\ttarget e7000 <device> <speed>\n\ |
| or \t\ttarget e7000 <host>[:<port>]\n\ |
| or \t\ttarget e7000 tcp_remote <host>[:<port>]\n\ |
| or \t\ttarget e7000 pc\n"); |
| } |
| |
| #if !defined(__GO32__) && !defined(_WIN32) |
| /* FIXME! test for ':' is ambiguous */ |
| if (n == 1 && strchr (dev_name, ':') == 0) |
| { |
| /* Default to normal telnet port */ |
| /* serial_open will use this to determine tcp communication */ |
| strcat (dev_name, ":23"); |
| } |
| #endif |
| if (!using_tcp_remote && strchr (dev_name, ':')) |
| using_tcp = 1; |
| } |
| |
| return n; |
| } |
| |
| /* Stub for catch_errors. */ |
| |
| static int |
| e7000_start_remote (dummy) |
| char *dummy; |
| { |
| int loop; |
| int sync; |
| int try; |
| int quit_trying; |
| |
| immediate_quit = 1; /* Allow user to interrupt it */ |
| |
| /* Hello? Are you there? */ |
| sync = 0; |
| loop = 0; |
| try = 0; |
| quit_trying = 20; |
| putchar_e7000 (CTRLC); |
| while (!sync && ++try <= quit_trying) |
| { |
| int c; |
| |
| printf_unfiltered ("[waiting for e7000...]\n"); |
| |
| write_e7000 ("\r"); |
| c = readchar (1); |
| |
| /* FIXME! this didn't seem right-> while (c != SERIAL_TIMEOUT) |
| * we get stuck in this loop ... |
| * We may never timeout, and never sync up :-( |
| */ |
| while (!sync && c != -1) |
| { |
| /* Dont echo cr's */ |
| if (c != '\r') |
| { |
| putchar_unfiltered (c); |
| gdb_flush (gdb_stdout); |
| } |
| /* Shouldn't we either break here, or check for sync in inner loop? */ |
| if (c == ':') |
| sync = 1; |
| |
| if (loop++ == 20) |
| { |
| putchar_e7000 (CTRLC); |
| loop = 0; |
| } |
| |
| QUIT; |
| |
| if (quit_flag) |
| { |
| putchar_e7000 (CTRLC); |
| /* Was-> quit_flag = 0; */ |
| c = -1; |
| quit_trying = try + 1; /* we don't want to try anymore */ |
| } |
| else |
| { |
| c = readchar (1); |
| } |
| } |
| } |
| |
| if (!sync) |
| { |
| fprintf_unfiltered (gdb_stderr, "Giving up after %d tries...\n", try); |
| error ("Unable to syncronize with target.\n"); |
| } |
| |
| puts_e7000debug ("\r"); |
| expect_prompt (); |
| puts_e7000debug ("b -\r"); /* Clear breakpoints */ |
| expect_prompt (); |
| |
| immediate_quit = 0; |
| |
| /* This is really the job of start_remote however, that makes an assumption |
| that the target is about to print out a status message of some sort. That |
| doesn't happen here. */ |
| |
| flush_cached_frames (); |
| registers_changed (); |
| stop_pc = read_pc (); |
| set_current_frame (create_new_frame (read_fp (), stop_pc)); |
| select_frame (get_current_frame (), 0); |
| print_stack_frame (selected_frame, -1, 1); |
| |
| return 1; |
| } |
| |
| static void |
| e7000_open (args, from_tty) |
| char *args; |
| int from_tty; |
| { |
| int n; |
| |
| target_preopen (from_tty); |
| |
| n = e7000_parse_device (args, dev_name, baudrate); |
| |
| push_target (&e7000_ops); |
| |
| e7000_desc = SERIAL_OPEN (dev_name); |
| |
| if (!e7000_desc) |
| perror_with_name (dev_name); |
| |
| SERIAL_SETBAUDRATE (e7000_desc, baudrate); |
| SERIAL_RAW (e7000_desc); |
| |
| #ifdef GDB_TARGET_IS_H8300 |
| h8300hmode = 1; |
| #endif |
| |
| /* Start the remote connection; if error (0), discard this target. |
| In particular, if the user quits, be sure to discard it |
| (we'd be in an inconsistent state otherwise). */ |
| if (!catch_errors (e7000_start_remote, (char *) 0, |
| "Couldn't establish connection to remote target\n", RETURN_MASK_ALL)) |
| if (from_tty) |
| printf_filtered ("Remote target %s connected to %s\n", target_shortname, |
| dev_name); |
| } |
| |
| /* Close out all files and local state before this target loses control. */ |
| |
| static void |
| e7000_close (quitting) |
| int quitting; |
| { |
| if (e7000_desc) |
| { |
| SERIAL_CLOSE (e7000_desc); |
| e7000_desc = 0; |
| } |
| } |
| |
| /* Terminate the open connection to the remote debugger. Use this |
| when you want to detach and do something else with your gdb. */ |
| |
| static void |
| e7000_detach (from_tty) |
| int from_tty; |
| { |
| pop_target (); /* calls e7000_close to do the real work */ |
| if (from_tty) |
| printf_unfiltered ("Ending remote %s debugging\n", target_shortname); |
| } |
| |
| /* Tell the remote machine to resume. */ |
| |
| static void |
| e7000_resume (pid, step, sig) |
| int pid, step, sig; |
| { |
| if (step) |
| puts_e7000debug ("S\r"); |
| else |
| puts_e7000debug ("G\r"); |
| } |
| |
| /* Read the remote registers into the block REGS. |
| |
| For the H8/300 a register dump looks like: |
| |
| PC=00021A CCR=80:I******* |
| ER0 - ER3 0000000A 0000002E 0000002E 00000000 |
| ER4 - ER7 00000000 00000000 00000000 00FFEFF6 |
| 000218 MOV.B R1L,R2L |
| STEP NORMAL END or |
| BREAK POINT |
| */ |
| |
| #ifdef GDB_TARGET_IS_H8300 |
| |
| char *want_h8300h = "PC=%p CCR=%c\n\ |
| ER0 - ER3 %0 %1 %2 %3\n\ |
| ER4 - ER7 %4 %5 %6 %7\n"; |
| |
| char *want_nopc_h8300h = "%p CCR=%c\n\ |
| ER0 - ER3 %0 %1 %2 %3\n\ |
| ER4 - ER7 %4 %5 %6 %7"; |
| |
| char *want_h8300s = "PC=%p CCR=%c\n\ |
| MACH=\n\ |
| ER0 - ER3 %0 %1 %2 %3\n\ |
| ER4 - ER7 %4 %5 %6 %7\n"; |
| |
| char *want_nopc_h8300s = "%p CCR=%c EXR=%9\n\ |
| ER0 - ER3 %0 %1 %2 %3\n\ |
| ER4 - ER7 %4 %5 %6 %7"; |
| |
| #endif |
| |
| #ifdef GDB_TARGET_IS_SH |
| |
| char *want = "PC=%16 SR=%22\n\ |
| PR=%17 GBR=%18 VBR=%19\n\ |
| MACH=%20 MACL=%21\n\ |
| R0-7 %0 %1 %2 %3 %4 %5 %6 %7\n\ |
| R8-15 %8 %9 %10 %11 %12 %13 %14 %15\n"; |
| |
| char *want_nopc = "%16 SR=%22\n\ |
| PR=%17 GBR=%18 VBR=%19\n\ |
| MACH=%20 MACL=%21\n\ |
| R0-7 %0 %1 %2 %3 %4 %5 %6 %7\n\ |
| R8-15 %8 %9 %10 %11 %12 %13 %14 %15"; |
| |
| char *want_sh3 = "PC=%16 SR=%22\n\ |
| PR=%17 GBR=%18 VBR=%19\n\ |
| MACH=%20 MACL=%21 SSR=%23 SPC=%24\n\ |
| R0-7 %0 %1 %2 %3 %4 %5 %6 %7\n\ |
| R8-15 %8 %9 %10 %11 %12 %13 %14 %15\n\ |
| R0_BANK0-R3_BANK0 %25 %26 %27 %28\n\ |
| R4_BANK0-R7_BANK0 %29 %30 %31 %32\n\ |
| R0_BANK1-R3_BANK1 %33 %34 %35 %36\n\ |
| R4_BANK1-R7_BANK1 %37 %38 %39 %40"; |
| |
| char *want_sh3_nopc = "%16 SR=%22\n\ |
| PR=%17 GBR=%18 VBR=%19\n\ |
| MACH=%20 MACL=%21 SSR=%22 SPC=%23\n\ |
| R0-7 %0 %1 %2 %3 %4 %5 %6 %7\n\ |
| R8-15 %8 %9 %10 %11 %12 %13 %14 %15\n\ |
| R0_BANK0-R3_BANK0 %25 %26 %27 %28\n\ |
| R4_BANK0-R7_BANK0 %29 %30 %31 %32\n\ |
| R0_BANK1-R3_BANK1 %33 %34 %35 %36\n\ |
| R4_BANK1-R7_BANK1 %37 %38 %39 %40"; |
| |
| #endif |
| |
| static int |
| gch () |
| { |
| return readchar (timeout); |
| } |
| |
| static unsigned int |
| gbyte () |
| { |
| int high = convert_hex_digit (gch ()); |
| int low = convert_hex_digit (gch ()); |
| |
| return (high << 4) + low; |
| } |
| |
| void |
| fetch_regs_from_dump (nextchar, want) |
| int (*nextchar) (); |
| char *want; |
| { |
| int regno; |
| char buf[MAX_REGISTER_RAW_SIZE]; |
| |
| int thischar = nextchar (); |
| |
| while (*want) |
| { |
| switch (*want) |
| { |
| case '\n': |
| /* Skip to end of line and then eat all new line type stuff */ |
| while (thischar != '\n' && thischar != '\r') |
| thischar = nextchar (); |
| while (thischar == '\n' || thischar == '\r') |
| thischar = nextchar (); |
| want++; |
| break; |
| |
| case ' ': |
| while (thischar == ' ' |
| || thischar == '\t' |
| || thischar == '\r' |
| || thischar == '\n') |
| thischar = nextchar (); |
| want++; |
| break; |
| |
| default: |
| if (*want == thischar) |
| { |
| want++; |
| if (*want) |
| thischar = nextchar (); |
| |
| } |
| else if (thischar == ' ' || thischar == '\n' || thischar == '\r') |
| { |
| thischar = nextchar (); |
| } |
| else |
| { |
| error ("out of sync in fetch registers wanted <%s>, got <%c 0x%x>", |
| want, thischar, thischar); |
| } |
| |
| break; |
| case '%': |
| /* Got a register command */ |
| want++; |
| switch (*want) |
| { |
| #ifdef PC_REGNUM |
| case 'p': |
| regno = PC_REGNUM; |
| want++; |
| break; |
| #endif |
| #ifdef CCR_REGNUM |
| case 'c': |
| regno = CCR_REGNUM; |
| want++; |
| break; |
| #endif |
| #ifdef SP_REGNUM |
| case 's': |
| regno = SP_REGNUM; |
| want++; |
| break; |
| #endif |
| #ifdef FP_REGNUM |
| case 'f': |
| regno = FP_REGNUM; |
| want++; |
| break; |
| #endif |
| |
| default: |
| if (isdigit (want[0])) |
| { |
| if (isdigit (want[1])) |
| { |
| regno = (want[0] - '0') * 10 + want[1] - '0'; |
| want += 2; |
| } |
| else |
| { |
| regno = want[0] - '0'; |
| want++; |
| } |
| } |
| |
| else |
| abort (); |
| } |
| store_signed_integer (buf, |
| REGISTER_RAW_SIZE (regno), |
| (LONGEST) get_hex (&thischar, nextchar)); |
| supply_register (regno, buf); |
| break; |
| } |
| } |
| } |
| |
| static void |
| e7000_fetch_registers () |
| { |
| int regno; |
| char *wanted; |
| |
| puts_e7000debug ("R\r"); |
| |
| #ifdef GDB_TARGET_IS_SH |
| wanted = want; |
| if (TARGET_ARCHITECTURE->arch == bfd_arch_sh) |
| switch (TARGET_ARCHITECTURE->mach) |
| { |
| case bfd_mach_sh3: |
| case bfd_mach_sh3e: |
| case bfd_mach_sh4: |
| wanted = want_sh3; |
| } |
| #else |
| if (h8300smode) |
| wanted = want_h8300s; |
| else |
| wanted = want_h8300h; |
| #endif |
| fetch_regs_from_dump (gch, wanted); |
| |
| /* And supply the extra ones the simulator uses */ |
| for (regno = NUM_REALREGS; regno < NUM_REGS; regno++) |
| { |
| int buf = 0; |
| |
| supply_register (regno, (char *) (&buf)); |
| } |
| } |
| |
| /* Fetch register REGNO, or all registers if REGNO is -1. Returns |
| errno value. */ |
| |
| static void |
| e7000_fetch_register (regno) |
| int regno; |
| { |
| e7000_fetch_registers (); |
| } |
| |
| /* Store the remote registers from the contents of the block REGS. */ |
| |
| static void |
| e7000_store_registers () |
| { |
| int regno; |
| |
| for (regno = 0; regno < NUM_REALREGS; regno++) |
| e7000_store_register (regno); |
| |
| registers_changed (); |
| } |
| |
| /* Store register REGNO, or all if REGNO == 0. Return errno value. */ |
| |
| static void |
| e7000_store_register (regno) |
| int regno; |
| { |
| char buf[200]; |
| |
| if (regno == -1) |
| { |
| e7000_store_registers (); |
| return; |
| } |
| |
| #ifdef GDB_TARGET_IS_H8300 |
| if (regno <= 7) |
| { |
| sprintf (buf, ".ER%d %x\r", regno, read_register (regno)); |
| puts_e7000debug (buf); |
| } |
| else if (regno == PC_REGNUM) |
| { |
| sprintf (buf, ".PC %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| } |
| else if (regno == CCR_REGNUM) |
| { |
| sprintf (buf, ".CCR %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| } |
| #endif /* GDB_TARGET_IS_H8300 */ |
| |
| #ifdef GDB_TARGET_IS_SH |
| switch (regno) |
| { |
| default: |
| sprintf (buf, ".R%d %x\r", regno, read_register (regno)); |
| puts_e7000debug (buf); |
| break; |
| |
| case PC_REGNUM: |
| sprintf (buf, ".PC %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| break; |
| |
| case SR_REGNUM: |
| sprintf (buf, ".SR %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| break; |
| |
| case PR_REGNUM: |
| sprintf (buf, ".PR %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| break; |
| |
| case GBR_REGNUM: |
| sprintf (buf, ".GBR %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| break; |
| |
| case VBR_REGNUM: |
| sprintf (buf, ".VBR %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| break; |
| |
| case MACH_REGNUM: |
| sprintf (buf, ".MACH %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| break; |
| |
| case MACL_REGNUM: |
| sprintf (buf, ".MACL %x\r", read_register (regno)); |
| puts_e7000debug (buf); |
| break; |
| } |
| |
| #endif /* GDB_TARGET_IS_SH */ |
| |
| 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. */ |
| |
| static void |
| e7000_prepare_to_store () |
| { |
| /* Do nothing, since we can store individual regs */ |
| } |
| |
| static void |
| e7000_files_info () |
| { |
| printf_unfiltered ("\tAttached to %s at %d baud.\n", dev_name, baudrate); |
| } |
| |
| static int |
| stickbyte (where, what) |
| char *where; |
| unsigned int what; |
| { |
| static CONST char digs[] = "0123456789ABCDEF"; |
| |
| where[0] = digs[(what >> 4) & 0xf]; |
| where[1] = digs[(what & 0xf) & 0xf]; |
| |
| return what; |
| } |
| |
| /* Write a small ammount of memory. */ |
| |
| static int |
| write_small (memaddr, myaddr, len) |
| CORE_ADDR memaddr; |
| unsigned char *myaddr; |
| int len; |
| { |
| int i; |
| char buf[200]; |
| |
| for (i = 0; i < len; i++) |
| { |
| if (((memaddr + i) & 3) == 0 && (i + 3 < len)) |
| { |
| /* Can be done with a long word */ |
| sprintf (buf, "m %x %x%02x%02x%02x;l\r", |
| memaddr + i, |
| myaddr[i], myaddr[i + 1], myaddr[i + 2], myaddr[i + 3]); |
| puts_e7000debug (buf); |
| i += 3; |
| } |
| else |
| { |
| sprintf (buf, "m %x %x\r", memaddr + i, myaddr[i]); |
| puts_e7000debug (buf); |
| } |
| } |
| |
| expect_prompt (); |
| |
| return len; |
| } |
| |
| /* Write a large ammount of memory, this only works with the serial |
| mode enabled. Command is sent as |
| |
| il ;s:s\r -> |
| <- il ;s:s\r |
| <- ENQ |
| ACK -> |
| <- LO s\r |
| Srecords... |
| ^Z -> |
| <- ENQ |
| ACK -> |
| <- : |
| */ |
| |
| static int |
| write_large (memaddr, myaddr, len) |
| CORE_ADDR memaddr; |
| unsigned char *myaddr; |
| int len; |
| { |
| int i; |
| #define maxstride 128 |
| int stride; |
| |
| puts_e7000debug ("IL ;S:FK\r"); |
| expect (ENQSTRING); |
| putchar_e7000 (ACK); |
| expect ("LO FK\r"); |
| |
| for (i = 0; i < len; i += stride) |
| { |
| char compose[maxstride * 2 + 50]; |
| int address = i + memaddr; |
| int j; |
| int check_sum; |
| int where = 0; |
| int alen; |
| |
| stride = len - i; |
| if (stride > maxstride) |
| stride = maxstride; |
| |
| compose[where++] = 'S'; |
| check_sum = 0; |
| if (address >= 0xffffff) |
| alen = 4; |
| else if (address >= 0xffff) |
| alen = 3; |
| else |
| alen = 2; |
| /* Insert type. */ |
| compose[where++] = alen - 1 + '0'; |
| /* Insert length. */ |
| check_sum += stickbyte (compose + where, alen + stride + 1); |
| where += 2; |
| while (alen > 0) |
| { |
| alen--; |
| check_sum += stickbyte (compose + where, address >> (8 * (alen))); |
| where += 2; |
| } |
| |
| for (j = 0; j < stride; j++) |
| { |
| check_sum += stickbyte (compose + where, myaddr[i + j]); |
| where += 2; |
| } |
| stickbyte (compose + where, ~check_sum); |
| where += 2; |
| compose[where++] = '\r'; |
| compose[where++] = '\n'; |
| compose[where++] = 0; |
| |
| SERIAL_WRITE (e7000_desc, compose, where); |
| j = readchar (0); |
| if (j == -1) |
| { |
| /* This is ok - nothing there */ |
| } |
| else if (j == ENQ) |
| { |
| /* Hmm, it's trying to tell us something */ |
| expect (":"); |
| error ("Error writing memory"); |
| } |
| else |
| { |
| printf_unfiltered ("@%d}@", j); |
| while ((j = readchar (0)) > 0) |
| { |
| printf_unfiltered ("@{%d}@", j); |
| } |
| } |
| } |
| |
| /* Send the trailer record */ |
| write_e7000 ("S70500000000FA\r"); |
| putchar_e7000 (CTRLZ); |
| expect (ENQSTRING); |
| putchar_e7000 (ACK); |
| expect (":"); |
| |
| return len; |
| } |
| |
| /* Copy LEN bytes of data from debugger memory at MYADDR to inferior's |
| memory at MEMADDR. Returns length moved. |
| |
| Can't use the Srecord load over ethernet, so don't use fast method |
| then. */ |
| |
| static int |
| e7000_write_inferior_memory (memaddr, myaddr, len) |
| CORE_ADDR memaddr; |
| unsigned char *myaddr; |
| int len; |
| { |
| if (len < 16 || using_tcp || using_pc) |
| return write_small (memaddr, myaddr, len); |
| else |
| return write_large (memaddr, myaddr, len); |
| } |
| |
| /* Read LEN bytes from inferior memory at MEMADDR. Put the result |
| at debugger address MYADDR. Returns length moved. |
| |
| Small transactions we send |
| m <addr>;l |
| and receive |
| 00000000 12345678 ? |
| */ |
| |
| static int |
| e7000_read_inferior_memory (memaddr, myaddr, len) |
| CORE_ADDR memaddr; |
| unsigned char *myaddr; |
| int len; |
| { |
| int count; |
| int c; |
| int i; |
| char buf[200]; |
| /* Starting address of this pass. */ |
| |
| /* printf("READ INF %x %x %d\n", memaddr, myaddr, len); */ |
| if (((memaddr - 1) + len) < memaddr) |
| { |
| errno = EIO; |
| return 0; |
| } |
| |
| sprintf (buf, "m %x;l\r", memaddr); |
| puts_e7000debug (buf); |
| |
| for (count = 0; count < len; count += 4) |
| { |
| /* Suck away the address */ |
| c = gch (); |
| while (c != ' ') |
| c = gch (); |
| c = gch (); |
| if (c == '*') |
| { /* Some kind of error */ |
| puts_e7000debug (".\r"); /* Some errors leave us in memory input mode */ |
| expect_full_prompt (); |
| return -1; |
| } |
| while (c != ' ') |
| c = gch (); |
| |
| /* Now read in the data */ |
| for (i = 0; i < 4; i++) |
| { |
| int b = gbyte (); |
| if (count + i < len) |
| { |
| myaddr[count + i] = b; |
| } |
| } |
| |
| /* Skip the trailing ? and send a . to end and a cr for more */ |
| gch (); |
| gch (); |
| if (count + 4 >= len) |
| puts_e7000debug (".\r"); |
| else |
| puts_e7000debug ("\r"); |
| |
| } |
| expect_prompt (); |
| return len; |
| } |
| |
| |
| |
| /* |
| For large transfers we used to send |
| |
| |
| d <addr> <endaddr>\r |
| |
| and receive |
| <ADDRESS> < D A T A > < ASCII CODE > |
| 00000000 5F FD FD FF DF 7F DF FF 01 00 01 00 02 00 08 04 "_..............." |
| 00000010 FF D7 FF 7F D7 F1 7F FF 00 05 00 00 08 00 40 00 "..............@." |
| 00000020 7F FD FF F7 7F FF FF F7 00 00 00 00 00 00 00 00 "................" |
| |
| A cost in chars for each transaction of 80 + 5*n-bytes. |
| |
| Large transactions could be done with the srecord load code, but |
| there is a pause for a second before dumping starts, which slows the |
| average rate down! |
| */ |
| |
| static int |
| e7000_read_inferior_memory_large (memaddr, myaddr, len) |
| CORE_ADDR memaddr; |
| unsigned char *myaddr; |
| int len; |
| { |
| int count; |
| int c; |
| char buf[200]; |
| |
| /* Starting address of this pass. */ |
| |
| if (((memaddr - 1) + len) < memaddr) |
| { |
| errno = EIO; |
| return 0; |
| } |
| |
| sprintf (buf, "d %x %x\r", memaddr, memaddr + len - 1); |
| puts_e7000debug (buf); |
| |
| count = 0; |
| c = gch (); |
| |
| /* skip down to the first ">" */ |
| while (c != '>') |
| c = gch (); |
| /* now skip to the end of that line */ |
| while (c != '\r') |
| c = gch (); |
| c = gch (); |
| |
| while (count < len) |
| { |
| /* get rid of any white space before the address */ |
| while (c <= ' ') |
| c = gch (); |
| |
| /* Skip the address */ |
| get_hex (&c); |
| |
| /* read in the bytes on the line */ |
| while (c != '"' && count < len) |
| { |
| if (c == ' ') |
| c = gch (); |
| else |
| { |
| myaddr[count++] = get_hex (&c); |
| } |
| } |
| /* throw out the rest of the line */ |
| while (c != '\r') |
| c = gch (); |
| } |
| |
| /* wait for the ":" prompt */ |
| while (c != ':') |
| c = gch (); |
| |
| return len; |
| } |
| |
| #if 0 |
| |
| static int |
| fast_but_for_the_pause_e7000_read_inferior_memory (memaddr, myaddr, len) |
| CORE_ADDR memaddr; |
| char *myaddr; |
| int len; |
| { |
| int loop; |
| int c; |
| char buf[200]; |
| |
| if (((memaddr - 1) + len) < memaddr) |
| { |
| errno = EIO; |
| return 0; |
| } |
| |
| sprintf (buf, "is %x@%x:s\r", memaddr, len); |
| puts_e7000debug (buf); |
| gch (); |
| c = gch (); |
| if (c != ENQ) |
| { |
| /* Got an error */ |
| error ("Memory read error"); |
| } |
| putchar_e7000 (ACK); |
| expect ("SV s"); |
| loop = 1; |
| while (loop) |
| { |
| int type; |
| int length; |
| int addr; |
| int i; |
| |
| c = gch (); |
| switch (c) |
| { |
| case ENQ: /* ENQ, at the end */ |
| loop = 0; |
| break; |
| case 'S': |
| /* Start of an Srecord */ |
| type = gch (); |
| length = gbyte (); |
| switch (type) |
| { |
| case '7': /* Termination record, ignore */ |
| case '0': |
| case '8': |
| case '9': |
| /* Header record - ignore it */ |
| while (length--) |
| { |
| gbyte (); |
| } |
| break; |
| case '1': |
| case '2': |
| case '3': |
| { |
| int alen; |
| |
| alen = type - '0' + 1; |
| addr = 0; |
| while (alen--) |
| { |
| addr = (addr << 8) + gbyte (); |
| length--; |
| } |
| |
| for (i = 0; i < length - 1; i++) |
| myaddr[i + addr - memaddr] = gbyte (); |
| |
| gbyte (); /* Ignore checksum */ |
| } |
| } |
| } |
| } |
| |
| putchar_e7000 (ACK); |
| expect ("TOP ADDRESS ="); |
| expect ("END ADDRESS ="); |
| expect (":"); |
| |
| return len; |
| } |
| |
| #endif |
| |
| static int |
| e7000_xfer_inferior_memory (memaddr, myaddr, len, write, target) |
| CORE_ADDR memaddr; |
| unsigned char *myaddr; |
| int len; |
| int write; |
| struct target_ops *target; /* ignored */ |
| { |
| if (write) |
| return e7000_write_inferior_memory (memaddr, myaddr, len); |
| else if (len < 16) |
| return e7000_read_inferior_memory (memaddr, myaddr, len); |
| else |
| return e7000_read_inferior_memory_large (memaddr, myaddr, len); |
| } |
| |
| static void |
| e7000_kill (args, from_tty) |
| char *args; |
| int from_tty; |
| { |
| } |
| |
| static void |
| e7000_load (args, from_tty) |
| char *args; |
| int from_tty; |
| { |
| struct cleanup *old_chain; |
| asection *section; |
| bfd *pbfd; |
| bfd_vma entry; |
| #define WRITESIZE 0x1000 |
| char buf[2 + 4 + 4 + WRITESIZE]; /* `DT' + <addr> + <len> + <data> */ |
| char *filename; |
| int quiet; |
| int nostart; |
| time_t start_time, end_time; /* Start and end times of download */ |
| unsigned long data_count; /* Number of bytes transferred to memory */ |
| int oldtimeout = timeout; |
| |
| timeout = remote_timeout; |
| |
| |
| /* FIXME! change test to test for type of download */ |
| if (!using_tcp) |
| { |
| generic_load (args, from_tty); |
| return; |
| } |
| |
| /* for direct tcp connections, we can do a fast binary download */ |
| buf[0] = 'D'; |
| buf[1] = 'T'; |
| quiet = 0; |
| nostart = 0; |
| filename = NULL; |
| |
| while (*args != '\000') |
| { |
| char *arg; |
| |
| while (isspace (*args)) |
| args++; |
| |
| arg = args; |
| |
| while ((*args != '\000') && !isspace (*args)) |
| args++; |
| |
| if (*args != '\000') |
| *args++ = '\000'; |
| |
| if (*arg != '-') |
| filename = arg; |
| else if (strncmp (arg, "-quiet", strlen (arg)) == 0) |
| quiet = 1; |
| else if (strncmp (arg, "-nostart", strlen (arg)) == 0) |
| nostart = 1; |
| else |
| error ("unknown option `%s'", arg); |
| } |
| |
| if (!filename) |
| filename = get_exec_file (1); |
| |
| pbfd = bfd_openr (filename, gnutarget); |
| if (pbfd == NULL) |
| { |
| perror_with_name (filename); |
| return; |
| } |
| old_chain = make_cleanup ((make_cleanup_func) bfd_close, pbfd); |
| |
| if (!bfd_check_format (pbfd, bfd_object)) |
| error ("\"%s\" is not an object file: %s", filename, |
| bfd_errmsg (bfd_get_error ())); |
| |
| start_time = time (NULL); |
| data_count = 0; |
| |
| puts_e7000debug ("mw\r"); |
| |
| expect ("\nOK"); |
| |
| for (section = pbfd->sections; section; section = section->next) |
| { |
| if (bfd_get_section_flags (pbfd, section) & SEC_LOAD) |
| { |
| bfd_vma section_address; |
| bfd_size_type section_size; |
| file_ptr fptr; |
| |
| section_address = bfd_get_section_vma (pbfd, section); |
| section_size = bfd_get_section_size_before_reloc (section); |
| |
| if (!quiet) |
| printf_filtered ("[Loading section %s at 0x%x (%d bytes)]\n", |
| bfd_get_section_name (pbfd, section), |
| section_address, |
| section_size); |
| |
| fptr = 0; |
| |
| data_count += section_size; |
| |
| while (section_size > 0) |
| { |
| int count; |
| static char inds[] = "|/-\\"; |
| static int k = 0; |
| |
| QUIT; |
| |
| count = min (section_size, WRITESIZE); |
| |
| buf[2] = section_address >> 24; |
| buf[3] = section_address >> 16; |
| buf[4] = section_address >> 8; |
| buf[5] = section_address; |
| |
| buf[6] = count >> 24; |
| buf[7] = count >> 16; |
| buf[8] = count >> 8; |
| buf[9] = count; |
| |
| bfd_get_section_contents (pbfd, section, buf + 10, fptr, count); |
| |
| if (SERIAL_WRITE (e7000_desc, buf, count + 10)) |
| fprintf_unfiltered (gdb_stderr, |
| "e7000_load: SERIAL_WRITE failed: %s\n", |
| safe_strerror (errno)); |
| |
| expect ("OK"); |
| |
| if (!quiet) |
| { |
| printf_unfiltered ("\r%c", inds[k++ % 4]); |
| gdb_flush (gdb_stdout); |
| } |
| |
| section_address += count; |
| fptr += count; |
| section_size -= count; |
| } |
| } |
| } |
| |
| write_e7000 ("ED"); |
| |
| expect_prompt (); |
| |
| end_time = time (NULL); |
| |
| /* Finally, make the PC point at the start address */ |
| |
| if (exec_bfd) |
| write_pc (bfd_get_start_address (exec_bfd)); |
| |
| inferior_pid = 0; /* No process now */ |
| |
| /* This is necessary because many things were based on the PC at the time that |
| we attached to the monitor, which is no longer valid now that we have loaded |
| new code (and just changed the PC). Another way to do this might be to call |
| normal_stop, except that the stack may not be valid, and things would get |
| horribly confused... */ |
| |
| clear_symtab_users (); |
| |
| if (!nostart) |
| { |
| entry = bfd_get_start_address (pbfd); |
| |
| if (!quiet) |
| printf_unfiltered ("[Starting %s at 0x%x]\n", filename, entry); |
| |
| /* start_routine (entry); */ |
| } |
| |
| report_transfer_performance (data_count, start_time, end_time); |
| |
| do_cleanups (old_chain); |
| timeout = oldtimeout; |
| } |
| |
| /* Clean up when a program exits. |
| |
| The program actually lives on in the remote processor's RAM, and may be |
| run again without a download. Don't leave it full of breakpoint |
| instructions. */ |
| |
| static void |
| e7000_mourn_inferior () |
| { |
| remove_breakpoints (); |
| unpush_target (&e7000_ops); |
| generic_mourn_inferior (); /* Do all the proper things now */ |
| } |
| |
| #define MAX_BREAKPOINTS 200 |
| #ifdef HARD_BREAKPOINTS |
| #define MAX_E7000DEBUG_BREAKPOINTS (BC_BREAKPOINTS ? 5 : MAX_BREAKPOINTS) |
| #else |
| #define MAX_E7000DEBUG_BREAKPOINTS MAX_BREAKPOINTS |
| #endif |
| |
| /* Since we can change to soft breakpoints dynamically, we must define |
| more than enough. Was breakaddr[MAX_E7000DEBUG_BREAKPOINTS]. */ |
| static CORE_ADDR breakaddr[MAX_BREAKPOINTS] = |
| {0}; |
| |
| static int |
| e7000_insert_breakpoint (addr, shadow) |
| CORE_ADDR addr; |
| unsigned char *shadow; |
| { |
| int i; |
| char buf[200]; |
| #if 0 |
| static char nop[2] = NOP; |
| #endif |
| |
| for (i = 0; i <= MAX_E7000DEBUG_BREAKPOINTS; i++) |
| if (breakaddr[i] == 0) |
| { |
| breakaddr[i] = addr; |
| /* Save old contents, and insert a nop in the space */ |
| #ifdef HARD_BREAKPOINTS |
| if (BC_BREAKPOINTS) |
| { |
| sprintf (buf, "BC%d A=%x\r", i + 1, addr); |
| puts_e7000debug (buf); |
| } |
| else |
| { |
| sprintf (buf, "B %x\r", addr); |
| puts_e7000debug (buf); |
| } |
| #else |
| #if 0 |
| e7000_read_inferior_memory (addr, shadow, 2); |
| e7000_write_inferior_memory (addr, nop, 2); |
| #endif |
| |
| sprintf (buf, "B %x\r", addr); |
| puts_e7000debug (buf); |
| #endif |
| expect_prompt (); |
| return 0; |
| } |
| |
| error ("Too many breakpoints ( > %d) for the E7000\n", |
| MAX_E7000DEBUG_BREAKPOINTS); |
| return 1; |
| } |
| |
| static int |
| e7000_remove_breakpoint (addr, shadow) |
| CORE_ADDR addr; |
| unsigned char *shadow; |
| { |
| int i; |
| char buf[200]; |
| |
| for (i = 0; i < MAX_E7000DEBUG_BREAKPOINTS; i++) |
| if (breakaddr[i] == addr) |
| { |
| breakaddr[i] = 0; |
| #ifdef HARD_BREAKPOINTS |
| if (BC_BREAKPOINTS) |
| { |
| sprintf (buf, "BC%d - \r", i + 1); |
| puts_e7000debug (buf); |
| } |
| else |
| { |
| sprintf (buf, "B - %x\r", addr); |
| puts_e7000debug (buf); |
| } |
| expect_prompt (); |
| #else |
| sprintf (buf, "B - %x\r", addr); |
| puts_e7000debug (buf); |
| expect_prompt (); |
| |
| #if 0 |
| /* Replace the insn under the break */ |
| e7000_write_inferior_memory (addr, shadow, 2); |
| #endif |
| #endif |
| |
| return 0; |
| } |
| |
| warning ("Can't find breakpoint associated with 0x%x\n", addr); |
| return 1; |
| } |
| |
| /* Put a command string, in args, out to STDBUG. Output from STDBUG |
| is placed on the users terminal until the prompt is seen. */ |
| |
| static void |
| e7000_command (args, fromtty) |
| char *args; |
| int fromtty; |
| { |
| /* FIXME: arbitrary limit on length of args. */ |
| char buf[200]; |
| |
| echo = 0; |
| |
| if (!e7000_desc) |
| error ("e7000 target not open."); |
| if (!args) |
| { |
| puts_e7000debug ("\r"); |
| } |
| else |
| { |
| sprintf (buf, "%s\r", args); |
| puts_e7000debug (buf); |
| } |
| |
| echo++; |
| ctrl_c = 2; |
| expect_full_prompt (); |
| echo--; |
| ctrl_c = 0; |
| printf_unfiltered ("\n"); |
| |
| /* Who knows what the command did... */ |
| registers_changed (); |
| } |
| |
| |
| static void |
| e7000_drain_command (args, fromtty) |
| char *args; |
| int fromtty; |
| |
| { |
| int c; |
| |
| puts_e7000debug ("end\r"); |
| putchar_e7000 (CTRLC); |
| |
| while ((c = readchar (1) != -1)) |
| { |
| if (quit_flag) |
| { |
| putchar_e7000 (CTRLC); |
| quit_flag = 0; |
| } |
| if (c > ' ' && c < 127) |
| printf_unfiltered ("%c", c & 0xff); |
| else |
| printf_unfiltered ("<%x>", c & 0xff); |
| } |
| } |
| |
| #define NITEMS 7 |
| |
| static int |
| why_stop () |
| { |
| static char *strings[NITEMS] = |
| { |
| "STEP NORMAL", |
| "BREAK POINT", |
| "BREAK KEY", |
| "BREAK CONDI", |
| "CYCLE ACCESS", |
| "ILLEGAL INSTRUCTION", |
| "WRITE PROTECT", |
| }; |
| char *p[NITEMS]; |
| int c; |
| int i; |
| |
| for (i = 0; i < NITEMS; ++i) |
| p[i] = strings[i]; |
| |
| c = gch (); |
| while (1) |
| { |
| for (i = 0; i < NITEMS; i++) |
| { |
| if (c == *(p[i])) |
| { |
| p[i]++; |
| if (*(p[i]) == 0) |
| { |
| /* found one of the choices */ |
| return i; |
| } |
| } |
| else |
| p[i] = strings[i]; |
| } |
| |
| c = gch (); |
| } |
| } |
| |
| /* Suck characters, if a string match, then return the strings index |
| otherwise echo them. */ |
| |
| int |
| expect_n (strings) |
| char **strings; |
| { |
| char *(ptr[10]); |
| int n; |
| int c; |
| char saveaway[100]; |
| char *buffer = saveaway; |
| /* Count number of expect strings */ |
| |
| for (n = 0; strings[n]; n++) |
| { |
| ptr[n] = strings[n]; |
| } |
| |
| while (1) |
| { |
| int i; |
| int gotone = 0; |
| |
| c = readchar (1); |
| if (c == -1) |
| { |
| printf_unfiltered ("[waiting for e7000...]\n"); |
| } |
| #ifdef __GO32__ |
| if (kbhit ()) |
| { |
| int k = getkey (); |
| |
| if (k == 1) |
| quit_flag = 1; |
| } |
| #endif |
| if (quit_flag) |
| { |
| putchar_e7000 (CTRLC); /* interrupt the running program */ |
| quit_flag = 0; |
| } |
| |
| for (i = 0; i < n; i++) |
| { |
| if (c == ptr[i][0]) |
| { |
| ptr[i]++; |
| if (ptr[i][0] == 0) |
| { |
| /* Gone all the way */ |
| return i; |
| } |
| gotone = 1; |
| } |
| else |
| { |
| ptr[i] = strings[i]; |
| } |
| } |
| |
| if (gotone) |
| { |
| /* Save it up incase we find that there was no match */ |
| *buffer++ = c; |
| } |
| else |
| { |
| if (buffer != saveaway) |
| { |
| *buffer++ = 0; |
| printf_unfiltered ("%s", buffer); |
| buffer = saveaway; |
| } |
| if (c != -1) |
| { |
| putchar_unfiltered (c); |
| gdb_flush (gdb_stdout); |
| } |
| } |
| } |
| } |
| |
| /* We subtract two from the pc here rather than use |
| DECR_PC_AFTER_BREAK since the e7000 doesn't always add two to the |
| pc, and the simulators never do. */ |
| |
| static void |
| sub2_from_pc () |
| { |
| char buf[4]; |
| char buf2[200]; |
| |
| store_signed_integer (buf, |
| REGISTER_RAW_SIZE (PC_REGNUM), |
| read_register (PC_REGNUM) - 2); |
| supply_register (PC_REGNUM, buf); |
| sprintf (buf2, ".PC %x\r", read_register (PC_REGNUM)); |
| puts_e7000debug (buf2); |
| } |
| |
| #define WAS_SLEEP 0 |
| #define WAS_INT 1 |
| #define WAS_RUNNING 2 |
| #define WAS_OTHER 3 |
| |
| static char *estrings[] = |
| { |
| "** SLEEP", |
| "BREAK !", |
| "** PC", |
| "PC", |
| NULL |
| }; |
| |
| /* Wait until the remote machine stops, then return, storing status in |
| STATUS just as `wait' would. */ |
| |
| static int |
| e7000_wait (pid, status) |
| int pid; |
| struct target_waitstatus *status; |
| { |
| int stop_reason; |
| int regno; |
| int running_count = 0; |
| int had_sleep = 0; |
| int loop = 1; |
| char *wanted_nopc; |
| |
| /* Then echo chars until PC= string seen */ |
| gch (); /* Drop cr */ |
| gch (); /* and space */ |
| |
| while (loop) |
| { |
| switch (expect_n (estrings)) |
| { |
| case WAS_OTHER: |
| /* how did this happen ? */ |
| loop = 0; |
| break; |
| case WAS_SLEEP: |
| had_sleep = 1; |
| putchar_e7000 (CTRLC); |
| loop = 0; |
| break; |
| case WAS_INT: |
| loop = 0; |
| break; |
| case WAS_RUNNING: |
| running_count++; |
| if (running_count == 20) |
| { |
| printf_unfiltered ("[running...]\n"); |
| running_count = 0; |
| } |
| break; |
| default: |
| /* error? */ |
| break; |
| } |
| } |
| |
| /* Skip till the PC= */ |
| expect ("="); |
| |
| #ifdef GDB_TARGET_IS_SH |
| wanted_nopc = want_nopc; |
| if (TARGET_ARCHITECTURE->arch == bfd_arch_sh) |
| switch (TARGET_ARCHITECTURE->mach) |
| { |
| case bfd_mach_sh3: |
| case bfd_mach_sh3e: |
| case bfd_mach_sh4: |
| wanted_nopc = want_sh3_nopc; |
| } |
| #else |
| if (h8300smode) |
| wanted_nopc = want_nopc_h8300s; |
| else |
| wanted_nopc = want_nopc_h8300h; |
| #endif |
| fetch_regs_from_dump (gch, wanted_nopc); |
| |
| /* And supply the extra ones the simulator uses */ |
| for (regno = NUM_REALREGS; regno < NUM_REGS; regno++) |
| { |
| int buf = 0; |
| supply_register (regno, (char *) &buf); |
| } |
| |
| stop_reason = why_stop (); |
| expect_full_prompt (); |
| |
| status->kind = TARGET_WAITKIND_STOPPED; |
| status->value.sig = TARGET_SIGNAL_TRAP; |
| |
| switch (stop_reason) |
| { |
| case 1: /* Breakpoint */ |
| write_pc (read_pc ()); /* PC is always off by 2 for breakpoints */ |
| status->value.sig = TARGET_SIGNAL_TRAP; |
| break; |
| case 0: /* Single step */ |
| status->value.sig = TARGET_SIGNAL_TRAP; |
| break; |
| case 2: /* Interrupt */ |
| if (had_sleep) |
| { |
| status->value.sig = TARGET_SIGNAL_TRAP; |
| sub2_from_pc (); |
| } |
| else |
| { |
| status->value.sig = TARGET_SIGNAL_INT; |
| } |
| break; |
| case 3: |
| break; |
| case 4: |
| printf_unfiltered ("a cycle address error?\n"); |
| status->value.sig = TARGET_SIGNAL_UNKNOWN; |
| break; |
| case 5: |
| status->value.sig = TARGET_SIGNAL_ILL; |
| break; |
| case 6: |
| status->value.sig = TARGET_SIGNAL_SEGV; |
| break; |
| case 7: /* Anything else (NITEMS + 1) */ |
| printf_unfiltered ("a write protect error?\n"); |
| status->value.sig = TARGET_SIGNAL_UNKNOWN; |
| break; |
| default: |
| /* Get the user's attention - this should never happen. */ |
| abort (); |
| } |
| |
| return 0; |
| } |
| |
| /* Stop the running program. */ |
| |
| static void |
| e7000_stop () |
| { |
| /* Sending a ^C is supposed to stop the running program. */ |
| putchar_e7000 (CTRLC); |
| } |
| |
| /* Define the target subroutine names. */ |
| |
| struct target_ops e7000_ops; |
| |
| static void |
| init_e7000_ops (void) |
| { |
| e7000_ops.to_shortname = "e7000"; |
| e7000_ops.to_longname = "Remote Hitachi e7000 target"; |
| e7000_ops.to_doc = "Use a remote Hitachi e7000 ICE connected by a serial line;\n\ |
| or a network connection.\n\ |
| Arguments are the name of the device for the serial line,\n\ |
| the speed to connect at in bits per second.\n\ |
| eg\n\ |
| target e7000 /dev/ttya 9600\n\ |
| target e7000 foobar"; |
| e7000_ops.to_open = e7000_open; |
| e7000_ops.to_close = e7000_close; |
| e7000_ops.to_attach = 0; |
| e7000_ops.to_post_attach = NULL; |
| e7000_ops.to_require_attach = NULL; |
| e7000_ops.to_detach = e7000_detach; |
| e7000_ops.to_require_detach = NULL; |
| e7000_ops.to_resume = e7000_resume; |
| e7000_ops.to_wait = e7000_wait; |
| e7000_ops.to_post_wait = NULL; |
| e7000_ops.to_fetch_registers = e7000_fetch_register; |
| e7000_ops.to_store_registers = e7000_store_register; |
| e7000_ops.to_prepare_to_store = e7000_prepare_to_store; |
| e7000_ops.to_xfer_memory = e7000_xfer_inferior_memory; |
| e7000_ops.to_files_info = e7000_files_info; |
| e7000_ops.to_insert_breakpoint = e7000_insert_breakpoint; |
| e7000_ops.to_remove_breakpoint = e7000_remove_breakpoint; |
| e7000_ops.to_terminal_init = 0; |
| e7000_ops.to_terminal_inferior = 0; |
| e7000_ops.to_terminal_ours_for_output = 0; |
| e7000_ops.to_terminal_ours = 0; |
| e7000_ops.to_terminal_info = 0; |
| e7000_ops.to_kill = e7000_kill; |
| e7000_ops.to_load = e7000_load; |
| e7000_ops.to_lookup_symbol = 0; |
| e7000_ops.to_create_inferior = e7000_create_inferior; |
| e7000_ops.to_post_startup_inferior = NULL; |
| e7000_ops.to_acknowledge_created_inferior = NULL; |
| e7000_ops.to_clone_and_follow_inferior = NULL; |
| e7000_ops.to_post_follow_inferior_by_clone = NULL; |
| e7000_ops.to_insert_fork_catchpoint = NULL; |
| e7000_ops.to_remove_fork_catchpoint = NULL; |
| e7000_ops.to_insert_vfork_catchpoint = NULL; |
| e7000_ops.to_remove_vfork_catchpoint = NULL; |
| e7000_ops.to_has_forked = NULL; |
| e7000_ops.to_has_vforked = NULL; |
| e7000_ops.to_can_follow_vfork_prior_to_exec = NULL; |
| e7000_ops.to_post_follow_vfork = NULL; |
| e7000_ops.to_insert_exec_catchpoint = NULL; |
| e7000_ops.to_remove_exec_catchpoint = NULL; |
| e7000_ops.to_has_execd = NULL; |
| e7000_ops.to_reported_exec_events_per_exec_call = NULL; |
| e7000_ops.to_has_exited = NULL; |
| e7000_ops.to_mourn_inferior = e7000_mourn_inferior; |
| e7000_ops.to_can_run = 0; |
| e7000_ops.to_notice_signals = 0; |
| e7000_ops.to_thread_alive = 0; |
| e7000_ops.to_stop = e7000_stop; |
| e7000_ops.to_pid_to_exec_file = NULL; |
| e7000_ops.to_core_file_to_sym_file = NULL; |
| e7000_ops.to_stratum = process_stratum; |
| e7000_ops.DONT_USE = 0; |
| e7000_ops.to_has_all_memory = 1; |
| e7000_ops.to_has_memory = 1; |
| e7000_ops.to_has_stack = 1; |
| e7000_ops.to_has_registers = 1; |
| e7000_ops.to_has_execution = 1; |
| e7000_ops.to_sections = 0; |
| e7000_ops.to_sections_end = 0; |
| e7000_ops.to_magic = OPS_MAGIC; |
| }; |
| |
| void |
| _initialize_remote_e7000 () |
| { |
| init_e7000_ops (); |
| add_target (&e7000_ops); |
| |
| add_com ("e7000", class_obscure, e7000_command, |
| "Send a command to the e7000 monitor."); |
| |
| add_com ("ftplogin", class_obscure, e7000_login_command, |
| "Login to machine and change to directory."); |
| |
| add_com ("ftpload", class_obscure, e7000_ftp_command, |
| "Fetch and load a file from previously described place."); |
| |
| add_com ("drain", class_obscure, e7000_drain_command, |
| "Drain pending e7000 text buffers."); |
| |
| add_show_from_set (add_set_cmd ("usehardbreakpoints", no_class, |
| var_integer, (char *) &use_hard_breakpoints, |
| "Set use of hardware breakpoints for all breakpoints.\n", &setlist), |
| &showlist); |
| } |