aboutsummaryrefslogtreecommitdiff
path: root/gdb/ocd.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/ocd.c')
-rw-r--r--gdb/ocd.c1444
1 files changed, 1444 insertions, 0 deletions
diff --git a/gdb/ocd.c b/gdb/ocd.c
new file mode 100644
index 0000000..343fa62
--- /dev/null
+++ b/gdb/ocd.c
@@ -0,0 +1,1444 @@
+/* Target communications support for Macraigor Systems' On-Chip Debugging
+ Copyright 1996, 1997 Free Software Foundation, Inc.
+
+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. */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "gdb_string.h"
+#include <fcntl.h>
+#include "frame.h"
+#include "inferior.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "target.h"
+#include "wait.h"
+#include "gdbcmd.h"
+#include "objfiles.h"
+#include "gdb-stabs.h"
+#include "dcache.h"
+#include <sys/types.h>
+#include <signal.h>
+#include "serial.h"
+#include "ocd.h"
+
+/* Prototypes for local functions */
+
+static int ocd_read_bytes PARAMS ((CORE_ADDR memaddr,
+ char *myaddr, int len));
+
+static int ocd_start_remote PARAMS ((PTR dummy));
+
+static int readchar PARAMS ((int timeout));
+
+static void reset_packet PARAMS ((void));
+
+static void output_packet PARAMS ((void));
+
+static int get_quoted_char PARAMS ((int timeout));
+
+static void put_quoted_char PARAMS ((int c));
+
+static void ocd_interrupt PARAMS ((int signo));
+
+static void ocd_interrupt_twice PARAMS ((int signo));
+
+static void interrupt_query PARAMS ((void));
+
+static unsigned char * ocd_do_command PARAMS ((int cmd, int *statusp, int *lenp));
+
+static void ocd_put_packet PARAMS ((unsigned char *packet, int pktlen));
+
+static unsigned char * ocd_get_packet PARAMS ((int cmd, int *pktlen, int timeout));
+
+static struct target_ops *current_ops = NULL;
+
+static int last_run_status;
+
+/* This was 5 seconds, which is a long time to sit and wait.
+ Unless this is going though some terminal server or multiplexer or
+ other form of hairy serial connection, I would think 2 seconds would
+ be plenty. */
+
+#if 0
+/* FIXME: Change to allow option to set timeout value on a per target
+ basis. */
+static int remote_timeout = 2;
+#endif
+
+/* Descriptor for I/O to remote machine. Initialize it to NULL so that
+ ocd_open knows that we don't have a file open when the program
+ starts. */
+static serial_t ocd_desc = NULL;
+
+void
+ocd_error (s, error_code)
+ char *s;
+ int error_code;
+{
+ char buf[100];
+
+ fputs_filtered (s, gdb_stderr);
+ fputs_filtered (" ", gdb_stderr);
+
+ switch (error_code)
+ {
+ case 0x1: s = "Unknown fault"; break;
+ case 0x2: s = "Power failed"; break;
+ case 0x3: s = "Cable disconnected"; break;
+ case 0x4: s = "Couldn't enter OCD mode"; break;
+ case 0x5: s = "Target stuck in reset"; break;
+ case 0x6: s = "OCD hasn't been initialized"; break;
+ case 0x7: s = "Write verify failed"; break;
+ case 0x8: s = "Reg buff error (during MPC5xx fp reg read/write)"; break;
+ case 0x9: s = "Invalid CPU register access attempt failed"; break;
+ case 0x11: s = "Bus error"; break;
+ case 0x12: s = "Checksum error"; break;
+ case 0x13: s = "Illegal command"; break;
+ case 0x14: s = "Parameter error"; break;
+ case 0x15: s = "Internal error"; break;
+ case 0x80: s = "Flash erase error"; break;
+ default:
+ sprintf (buf, "Unknown error code %d", error_code);
+ s = buf;
+ }
+
+ error (s);
+}
+
+/* Return nonzero if the thread TH is still alive on the remote system. */
+
+int
+ocd_thread_alive (th)
+ int th;
+{
+ return 1;
+}
+
+/* Clean up connection to a remote debugger. */
+
+/* ARGSUSED */
+void
+ocd_close (quitting)
+ int quitting;
+{
+ if (ocd_desc)
+ SERIAL_CLOSE (ocd_desc);
+ ocd_desc = NULL;
+}
+
+/* Stub for catch_errors. */
+
+static int
+ocd_start_remote (dummy)
+ PTR dummy;
+{
+ unsigned char buf[10], *p;
+ int pktlen;
+ int status;
+ int error_code;
+ int speed;
+ enum ocd_target_type target_type;
+
+ target_type = *(enum ocd_target_type*)dummy;
+
+ immediate_quit = 1; /* Allow user to interrupt it */
+
+ SERIAL_SEND_BREAK (ocd_desc); /* Wake up the wiggler */
+
+ speed = 80; /* Divide clock by 4000 */
+
+ buf[0] = OCD_INIT;
+ buf[1] = speed >> 8;
+ buf[2] = speed & 0xff;
+ buf[3] = target_type;
+ ocd_put_packet (buf, 4); /* Init OCD params */
+ p = ocd_get_packet (buf[0], &pktlen, remote_timeout);
+
+ if (pktlen < 2)
+ error ("Truncated response packet from OCD device");
+
+ status = p[1];
+ error_code = p[2];
+
+ if (error_code != 0)
+ ocd_error ("OCD_INIT:", error_code);
+
+ ocd_do_command (OCD_AYT, &status, &pktlen);
+
+ p = ocd_do_command (OCD_GET_VERSION, &status, &pktlen);
+
+ printf_unfiltered ("[Wiggler version %x.%x, capability 0x%x]\n",
+ p[0], p[1], (p[2] << 16) | p[3]);
+
+#if 0
+ /* Reset the target */
+
+ ocd_do_command (OCD_RESET_RUN, &status, &pktlen);
+/* ocd_do_command (OCD_RESET, &status, &pktlen);*/
+#endif
+
+ /* If processor is still running, stop it. */
+
+ if (!(status & OCD_FLAG_BDM))
+ ocd_stop ();
+
+#if 1
+ /* When using a target box, we want to asynchronously return status when
+ target stops. The OCD_SET_CTL_FLAGS command is ignored by Wigglers.dll
+ when using a parallel Wiggler */
+ buf[0] = OCD_SET_CTL_FLAGS;
+ buf[1] = 0;
+ buf[2] = 1;
+ ocd_put_packet (buf, 3);
+
+ p = ocd_get_packet (buf[0], &pktlen, remote_timeout);
+
+ if (pktlen < 2)
+ error ("Truncated response packet from OCD device");
+
+ status = p[1];
+ error_code = p[2];
+
+ if (error_code != 0)
+ ocd_error ("OCD_SET_CTL_FLAGS:", error_code);
+#endif
+
+ 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 (in fact, it may not be possible to get the monitor to
+ send the appropriate packet). */
+
+ 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);
+
+ buf[0] = OCD_LOG_FILE;
+ buf[1] = 3; /* close existing WIGGLERS.LOG */
+ ocd_put_packet (buf, 2);
+ p = ocd_get_packet (buf[0], &pktlen, remote_timeout);
+
+ buf[0] = OCD_LOG_FILE;
+ buf[1] = 2; /* append to existing WIGGLERS.LOG */
+ ocd_put_packet (buf, 2);
+ p = ocd_get_packet (buf[0], &pktlen, remote_timeout);
+
+ return 1;
+}
+
+/* Open a connection to a remote debugger.
+ NAME is the filename used for communication. */
+
+static DCACHE *ocd_dcache;
+
+void
+ocd_open (name, from_tty, target_type, ops)
+ char *name;
+ int from_tty;
+ enum ocd_target_type target_type;
+ struct target_ops *ops;
+{
+ unsigned char buf[10], *p;
+ int status;
+ int pktlen;
+
+ if (name == 0)
+ error ("To open an OCD connection, you need to specify the\n\
+device the OCD device is attached to (e.g. /dev/ttya).");
+
+ target_preopen (from_tty);
+
+ current_ops = ops;
+
+ unpush_target (current_ops);
+
+ ocd_dcache = dcache_init (ocd_read_bytes, ocd_write_bytes);
+
+ if (strncmp(name,"wiggler",7) == 0)
+ {
+ ocd_desc = SERIAL_OPEN ("ocd");
+ if (!ocd_desc)
+ perror_with_name (name);
+
+ buf[0] = OCD_LOG_FILE;
+ buf[1] = 1; /* open new or overwrite existing WIGGLERS.LOG */
+ ocd_put_packet (buf, 2);
+ p = ocd_get_packet (buf[0], &pktlen, remote_timeout);
+
+ buf[0] = OCD_SET_CONNECTION;
+ buf[1] = 0x01; /* atoi (name[11]); */
+ ocd_put_packet (buf, 2);
+ p = ocd_get_packet (buf[0], &pktlen, remote_timeout);
+ }
+ else /* not using Wigglers.dll */
+ {
+ ocd_desc = SERIAL_OPEN (name);
+ if (!ocd_desc)
+ perror_with_name (name);
+ }
+
+ if (baud_rate != -1)
+ {
+ if (SERIAL_SETBAUDRATE (ocd_desc, baud_rate))
+ {
+ SERIAL_CLOSE (ocd_desc);
+ perror_with_name (name);
+ }
+ }
+
+ SERIAL_RAW (ocd_desc);
+
+ /* If there is something sitting in the buffer we might take it as a
+ response to a command, which would be bad. */
+ SERIAL_FLUSH_INPUT (ocd_desc);
+
+ if (from_tty)
+ {
+ puts_filtered ("Remote target wiggler connected to ");
+ puts_filtered (name);
+ puts_filtered ("\n");
+ }
+ push_target (current_ops); /* Switch to using remote target now */
+
+ /* Without this, some commands which require an active target (such as kill)
+ won't work. This variable serves (at least) double duty as both the pid
+ of the target process (if it has such), and as a flag indicating that a
+ target is active. These functions should be split out into seperate
+ variables, especially since GDB will someday have a notion of debugging
+ several processes. */
+
+ inferior_pid = 42000;
+ /* 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 (ocd_start_remote, &target_type,
+ "Couldn't establish connection to remote target\n",
+ RETURN_MASK_ALL))
+ {
+ pop_target();
+ error ("Failed to connect to OCD.");
+ }
+}
+
+/* This takes a program previously attached to and detaches it. After
+ this is done, GDB can be used to debug some other program. We
+ better not have left any breakpoints in the target program or it'll
+ die when it hits one. */
+
+void
+ocd_detach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (args)
+ error ("Argument given to \"detach\" when remotely debugging.");
+
+ pop_target ();
+ if (from_tty)
+ puts_filtered ("Ending remote debugging.\n");
+}
+
+/* Tell the remote machine to resume. */
+
+void
+ocd_resume (pid, step, siggnal)
+ int pid, step;
+ enum target_signal siggnal;
+{
+ int pktlen;
+
+ dcache_flush (ocd_dcache);
+
+ if (step)
+ ocd_do_command (OCD_STEP, &last_run_status, &pktlen);
+ else
+ ocd_do_command (OCD_RUN, &last_run_status, &pktlen);
+}
+
+void
+ocd_stop ()
+{
+ int status;
+ int pktlen;
+
+ ocd_do_command (OCD_STOP, &status, &pktlen);
+
+ if (!(status & OCD_FLAG_BDM))
+ error ("Can't stop target via BDM");
+}
+
+static volatile int ocd_interrupt_flag;
+
+/* Send ^C to target to halt it. Target will respond, and send us a
+ packet. */
+
+static void
+ocd_interrupt (signo)
+ int signo;
+{
+ /* If this doesn't work, try more severe steps. */
+ signal (signo, ocd_interrupt_twice);
+
+ if (remote_debug)
+ printf_unfiltered ("ocd_interrupt called\n");
+
+ {
+ char buf[1];
+
+ ocd_stop ();
+ buf[0] = OCD_AYT;
+ ocd_put_packet (buf, 1);
+ ocd_interrupt_flag = 1;
+ }
+}
+
+static void (*ofunc)();
+
+/* The user typed ^C twice. */
+static void
+ocd_interrupt_twice (signo)
+ int signo;
+{
+ signal (signo, ofunc);
+
+ interrupt_query ();
+
+ signal (signo, ocd_interrupt);
+}
+
+/* Ask the user what to do when an interrupt is received. */
+
+static void
+interrupt_query ()
+{
+ target_terminal_ours ();
+
+ if (query ("Interrupted while waiting for the program.\n\
+Give up (and stop debugging it)? "))
+ {
+ target_mourn_inferior ();
+ return_to_top_level (RETURN_QUIT);
+ }
+
+ target_terminal_inferior ();
+}
+
+/* If nonzero, ignore the next kill. */
+static int kill_kludge;
+
+/* Wait until the remote machine stops, then return,
+ storing status in STATUS just as `wait' would.
+ Returns "pid" (though it's not clear what, if anything, that
+ means in the case of this target). */
+
+int
+ocd_wait ()
+{
+ unsigned char *p;
+ int error_code;
+ int pktlen;
+ char buf[1];
+
+ ocd_interrupt_flag = 0;
+
+ /* Target might already be stopped by the time we get here. */
+ /* If we aren't already stopped, we need to loop until we've dropped
+ back into BDM mode */
+
+ while (!(last_run_status & OCD_FLAG_BDM))
+ {
+ buf[0] = OCD_AYT;
+ ocd_put_packet (buf, 1);
+ p = ocd_get_packet (OCD_AYT, &pktlen, -1);
+
+ ofunc = (void (*)()) signal (SIGINT, ocd_interrupt);
+ signal (SIGINT, ofunc);
+
+ if (pktlen < 2)
+ error ("Truncated response packet from OCD device");
+
+ last_run_status = p[1];
+ error_code = p[2];
+
+ if (error_code != 0)
+ ocd_error ("target_wait:", error_code);
+
+ if (last_run_status & OCD_FLAG_PWF)
+ error ("OCD device lost VCC at BDM interface.");
+ else if (last_run_status & OCD_FLAG_CABLE_DISC)
+ error ("OCD device cable appears to have been disconnected.");
+ }
+
+ if (ocd_interrupt_flag)
+ return 1;
+ else
+ return 0;
+}
+
+/* Read registers from the OCD device. Specify the starting and ending
+ register number. Return the number of regs actually read in *NUMREGS.
+ Returns a pointer to a static array containing the register contents. */
+
+unsigned char *
+ocd_read_bdm_registers (first_bdm_regno, last_bdm_regno, reglen)
+ int first_bdm_regno;
+ int last_bdm_regno;
+ int *reglen;
+{
+ unsigned char buf[10];
+ int i;
+ unsigned char *p;
+ unsigned char *regs;
+ int error_code, status;
+ int pktlen;
+
+ buf[0] = OCD_READ_REGS;
+ buf[1] = first_bdm_regno >> 8;
+ buf[2] = first_bdm_regno & 0xff;
+ buf[3] = last_bdm_regno >> 8;
+ buf[4] = last_bdm_regno & 0xff;
+
+ ocd_put_packet (buf, 5);
+ p = ocd_get_packet (OCD_READ_REGS, &pktlen, remote_timeout);
+
+ status = p[1];
+ error_code = p[2];
+
+ if (error_code != 0)
+ ocd_error ("read_bdm_registers:", error_code);
+
+ i = p[3];
+ if (i == 0)
+ i = 256;
+
+ if (i > pktlen - 4
+ || ((i & 3) != 0))
+ error ("Register block size bad: %d", i);
+
+ *reglen = i;
+
+ regs = p + 4;
+
+ return regs;
+}
+
+/* Read register BDM_REGNO and returns its value ala read_register() */
+
+CORE_ADDR
+ocd_read_bdm_register (bdm_regno)
+ int bdm_regno;
+{
+ int reglen;
+ unsigned char *p;
+ CORE_ADDR regval;
+
+ p = ocd_read_bdm_registers (bdm_regno, bdm_regno, &reglen);
+ regval = extract_unsigned_integer (p, reglen);
+
+ return regval;
+}
+
+void
+ocd_write_bdm_registers (first_bdm_regno, regptr, reglen)
+ int first_bdm_regno;
+ unsigned char *regptr;
+ int reglen;
+{
+ unsigned char *buf;
+ unsigned char *p;
+ int error_code, status;
+ int pktlen;
+
+ buf = alloca (4 + reglen);
+
+ buf[0] = OCD_WRITE_REGS;
+ buf[1] = first_bdm_regno >> 8;
+ buf[2] = first_bdm_regno & 0xff;
+ buf[3] = reglen;
+ memcpy (buf + 4, regptr, reglen);
+
+ ocd_put_packet (buf, 4 + reglen);
+ p = ocd_get_packet (OCD_WRITE_REGS, &pktlen, remote_timeout);
+
+ if (pktlen < 3)
+ error ("Truncated response packet from OCD device");
+
+ status = p[1];
+ error_code = p[2];
+
+ if (error_code != 0)
+ ocd_error ("ocd_write_bdm_registers:", error_code);
+}
+
+void
+ocd_write_bdm_register (bdm_regno, reg)
+ int bdm_regno;
+ CORE_ADDR reg;
+{
+ unsigned char buf[4];
+
+ store_unsigned_integer (buf, 4, reg);
+
+ ocd_write_bdm_registers (bdm_regno, buf, 4);
+}
+
+void
+ocd_prepare_to_store ()
+{
+}
+
+/* Write memory data directly to the remote machine.
+ This does not inform the data cache; the data cache uses this.
+ MEMADDR is the address in the remote memory space.
+ MYADDR is the address of the buffer in our space.
+ LEN is the number of bytes.
+
+ Returns number of bytes transferred, or 0 for error. */
+
+static int write_mem_command = OCD_WRITE_MEM;
+
+int
+ocd_write_bytes (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ char buf[256 + 10];
+ unsigned char *p;
+ int origlen;
+
+ origlen = len;
+
+ buf[0] = write_mem_command;
+ buf[5] = 1; /* Write as bytes */
+ buf[6] = 0; /* Don't verify */
+
+ while (len > 0)
+ {
+ int numbytes;
+ int pktlen;
+ int status, error_code;
+
+ numbytes = min (len, 256 - 8);
+
+ buf[1] = memaddr >> 24;
+ buf[2] = memaddr >> 16;
+ buf[3] = memaddr >> 8;
+ buf[4] = memaddr;
+
+ buf[7] = numbytes;
+
+ memcpy (&buf[8], myaddr, numbytes);
+ ocd_put_packet (buf, 8 + numbytes);
+ p = ocd_get_packet (OCD_WRITE_MEM, &pktlen, remote_timeout);
+ if (pktlen < 3)
+ error ("Truncated response packet from OCD device");
+
+ status = p[1];
+ error_code = p[2];
+
+ if (error_code == 0x11) /* Got a bus error? */
+ {
+ CORE_ADDR error_address;
+
+ error_address = p[3] << 24;
+ error_address |= p[4] << 16;
+ error_address |= p[5] << 8;
+ error_address |= p[6];
+ numbytes = error_address - memaddr;
+
+ len -= numbytes;
+
+ errno = EIO;
+
+ break;
+ }
+ else if (error_code != 0)
+ ocd_error ("ocd_write_bytes:", error_code);
+
+ len -= numbytes;
+ memaddr += numbytes;
+ myaddr += numbytes;
+ }
+
+ return origlen - len;
+}
+
+/* Read memory data directly from the remote machine.
+ This does not use the data cache; the data cache uses this.
+ MEMADDR is the address in the remote memory space.
+ MYADDR is the address of the buffer in our space.
+ LEN is the number of bytes.
+
+ Returns number of bytes transferred, or 0 for error. */
+
+static int
+ocd_read_bytes (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ char buf[256 + 10];
+ unsigned char *p;
+ int origlen;
+
+ origlen = len;
+
+ buf[0] = OCD_READ_MEM;
+ buf[5] = 1; /* Read as bytes */
+
+ while (len > 0)
+ {
+ int numbytes;
+ int pktlen;
+ int status, error_code;
+
+ numbytes = min (len, 256 - 7);
+
+ buf[1] = memaddr >> 24;
+ buf[2] = memaddr >> 16;
+ buf[3] = memaddr >> 8;
+ buf[4] = memaddr;
+
+ buf[6] = numbytes;
+
+ ocd_put_packet (buf, 7);
+ p = ocd_get_packet (OCD_READ_MEM, &pktlen, remote_timeout);
+ if (pktlen < 4)
+ error ("Truncated response packet from OCD device");
+
+ status = p[1];
+ error_code = p[2];
+
+ if (error_code == 0x11) /* Got a bus error? */
+ {
+ CORE_ADDR error_address;
+
+ error_address = p[3] << 24;
+ error_address |= p[4] << 16;
+ error_address |= p[5] << 8;
+ error_address |= p[6];
+ numbytes = error_address - memaddr;
+
+ len -= numbytes;
+
+ errno = EIO;
+
+ break;
+ }
+ else if (error_code != 0)
+ ocd_error ("ocd_read_bytes:", error_code);
+
+ memcpy (myaddr, &p[4], numbytes);
+
+ len -= numbytes;
+ memaddr += numbytes;
+ myaddr += numbytes;
+ }
+
+ return origlen - len;
+}
+
+/* Read or write LEN bytes from inferior memory at MEMADDR, transferring
+ to or from debugger address MYADDR. Write to inferior if SHOULD_WRITE is
+ nonzero. Returns length of data written or read; 0 for error. */
+
+/* ARGSUSED */
+int
+ocd_xfer_memory (memaddr, myaddr, len, should_write, target)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+ int should_write;
+ struct target_ops *target; /* ignored */
+{
+ return dcache_xfer_memory (ocd_dcache, memaddr, myaddr, len, should_write);
+}
+
+void
+ocd_files_info (ignore)
+ struct target_ops *ignore;
+{
+ puts_filtered ("Debugging a target over a serial line.\n");
+}
+
+/* Stuff for dealing with the packets which are part of this protocol.
+ See comment at top of file for details. */
+
+/* Read a single character from the remote side, handling wierd errors. */
+
+static int
+readchar (timeout)
+ int timeout;
+{
+ int ch;
+
+ ch = SERIAL_READCHAR (ocd_desc, timeout);
+
+ switch (ch)
+ {
+ case SERIAL_EOF:
+ error ("Remote connection closed");
+ case SERIAL_ERROR:
+ perror_with_name ("Remote communication error");
+ case SERIAL_TIMEOUT:
+ default:
+ return ch;
+ }
+}
+
+#if 0
+/* Read a character from the data stream, dequoting as necessary. SYN is
+ treated special. Any SYNs appearing in the data stream are returned as the
+ distinct value RAW_SYN (which has a value > 8 bits and therefore cannot be
+ mistaken for real data). */
+
+static int
+get_quoted_char (timeout)
+ int timeout;
+{
+ int ch;
+
+ ch = readchar (timeout);
+
+ switch (ch)
+ {
+ case SERIAL_TIMEOUT:
+ error ("Timeout in mid-packet, aborting");
+ case SYN:
+ return RAW_SYN;
+ case DLE:
+ ch = readchar (timeout);
+ if (ch == SYN)
+ return RAW_SYN;
+ return ch & ~0100;
+ default:
+ return ch;
+ }
+}
+
+static unsigned char pkt[256 * 2 + 10], *pktp; /* Worst case */
+
+static void
+reset_packet ()
+{
+ pktp = pkt;
+}
+
+static void
+output_packet ()
+{
+ if (SERIAL_WRITE (ocd_desc, pkt, pktp - pkt))
+ perror_with_name ("output_packet: write failed");
+
+ reset_packet ();
+}
+
+/* Output a quoted character. SYNs and DLEs are quoted. Everything else goes
+ through untouched. */
+
+static void
+put_quoted_char (c)
+ int c;
+{
+ switch (c)
+ {
+ case SYN:
+ case DLE:
+ *pktp++ = DLE;
+ c |= 0100;
+ }
+
+ *pktp++ = c;
+}
+
+/* Send a packet to the OCD device. The packet framed by a SYN character,
+ a byte count and a checksum. The byte count only counts the number of
+ bytes between the count and the checksum. A count of zero actually
+ means 256. Any SYNs within the packet (including the checksum and
+ count) must be quoted. The quote character must be quoted as well.
+ Quoting is done by replacing the character with the two-character sequence
+ DLE, {char} | 0100. Note that the quoting mechanism has no effect on the
+ byte count. */
+
+static void
+stu_put_packet (buf, len)
+ unsigned char *buf;
+ int len;
+{
+ unsigned char checksum;
+ unsigned char c;
+
+ if (len == 0 || len > 256)
+ abort (); /* Can't represent 0 length packet */
+
+ reset_packet ();
+
+ checksum = 0;
+
+ put_quoted_char (RAW_SYN);
+
+ c = len;
+
+ do
+ {
+ checksum += c;
+
+ put_quoted_char (c);
+
+ c = *buf++;
+ }
+ while (len-- > 0);
+
+ put_quoted_char (-checksum & 0xff);
+
+ output_packet ();
+}
+
+#else
+
+/* Send a packet to the OCD device. The packet framed by a SYN character,
+ a byte count and a checksum. The byte count only counts the number of
+ bytes between the count and the checksum. A count of zero actually
+ means 256. Any SYNs within the packet (including the checksum and
+ count) must be quoted. The quote character must be quoted as well.
+ Quoting is done by replacing the character with the two-character sequence
+ DLE, {char} | 0100. Note that the quoting mechanism has no effect on the
+ byte count. */
+
+static void
+ocd_put_packet (buf, len)
+ unsigned char *buf;
+ int len;
+{
+ unsigned char checksum;
+ unsigned char c;
+ unsigned char *packet, *packet_ptr;
+
+ packet = alloca (len + 1 + 1); /* packet + SYN + checksum */
+ packet_ptr = packet;
+
+ checksum = 0;
+
+ *packet_ptr++ = 0x55;
+
+ while (len-- > 0)
+ {
+ c = *buf++;
+
+ checksum += c;
+ *packet_ptr++ = c;
+ }
+
+ *packet_ptr++ = -checksum;
+ if (SERIAL_WRITE (ocd_desc, packet, packet_ptr - packet))
+ perror_with_name ("output_packet: write failed");
+}
+#endif
+
+#if 0
+/* Get a packet from the OCD device. Timeout is only enforced for the
+ first byte of the packet. Subsequent bytes are expected to arrive in
+ time <= remote_timeout. Returns a pointer to a static buffer containing
+ the payload of the packet. *LENP contains the length of the packet.
+*/
+
+static unsigned char *
+stu_get_packet (cmd, lenp, timeout)
+ unsigned char cmd;
+ int *lenp;
+{
+ int ch;
+ int len;
+ static unsigned char buf[256 + 10], *p;
+ unsigned char checksum;
+
+ find_packet:
+
+ ch = get_quoted_char (timeout);
+
+ if (ch < 0)
+ error ("get_packet (readchar): %d", ch);
+
+ if (ch != RAW_SYN)
+ goto find_packet;
+
+ found_syn: /* Found the start of a packet */
+
+ p = buf;
+ checksum = 0;
+
+ len = get_quoted_char (remote_timeout);
+
+ if (len == RAW_SYN)
+ goto found_syn;
+
+ checksum += len;
+
+ if (len == 0)
+ len = 256;
+
+ len++; /* Include checksum */
+
+ while (len-- > 0)
+ {
+ ch = get_quoted_char (remote_timeout);
+ if (ch == RAW_SYN)
+ goto found_syn;
+
+ *p++ = ch;
+ checksum += ch;
+ }
+
+ if (checksum != 0)
+ goto find_packet;
+
+ if (cmd != buf[0])
+ error ("Response phase error. Got 0x%x, expected 0x%x", buf[0], cmd);
+
+ *lenp = p - buf - 1;
+ return buf;
+}
+
+#else
+
+/* Get a packet from the OCD device. Timeout is only enforced for the
+ first byte of the packet. Subsequent bytes are expected to arrive in
+ time <= remote_timeout. Returns a pointer to a static buffer containing
+ the payload of the packet. *LENP contains the length of the packet.
+*/
+
+static unsigned char *
+ocd_get_packet (cmd, lenp, timeout)
+ int cmd;
+ int *lenp;
+{
+ int ch;
+ int len;
+ int i;
+ static unsigned char packet[512];
+ unsigned char *packet_ptr;
+ unsigned char checksum;
+
+ ch = readchar (timeout);
+
+ if (ch < 0)
+ error ("ocd_get_packet (readchar): %d", ch);
+
+ if (ch != 0x55)
+ error ("ocd_get_packet (readchar): %d", ch);
+
+/* Found the start of a packet */
+
+ packet_ptr = packet;
+ checksum = 0;
+
+/* Read command char. That sort of tells us how long the packet is. */
+
+ ch = readchar (timeout);
+
+ if (ch < 0)
+ error ("ocd_get_packet (readchar): %d", ch);
+
+ *packet_ptr++ = ch;
+ checksum += ch;
+
+/* Get status. */
+
+ ch = readchar (timeout);
+
+ if (ch < 0)
+ error ("ocd_get_packet (readchar): %d", ch);
+ *packet_ptr++ = ch;
+ checksum += ch;
+
+/* Get error code. */
+
+ ch = readchar (timeout);
+
+ if (ch < 0)
+ error ("ocd_get_packet (readchar): %d", ch);
+ *packet_ptr++ = ch;
+ checksum += ch;
+
+ switch (ch) /* Figure out length of packet */
+ {
+ case 0x7: /* Write verify error? */
+ len = 8; /* write address, value read back */
+ break;
+ case 0x11: /* Bus error? */
+ /* write address, read flag */
+ case 0x15: /* Internal error */
+ len = 5; /* error code, vector */
+ break;
+ default: /* Error w/no params */
+ len = 0;
+ break;
+ case 0x0: /* Normal result */
+ switch (packet[0])
+ {
+ case OCD_AYT: /* Are You There? */
+ case OCD_SET_BAUD_RATE: /* Set Baud Rate */
+ case OCD_INIT: /* Initialize OCD device */
+ case OCD_SET_SPEED: /* Set Speed */
+ case OCD_SET_FUNC_CODE: /* Set Function Code */
+ case OCD_SET_CTL_FLAGS: /* Set Control Flags */
+ case OCD_SET_BUF_ADDR: /* Set Register Buffer Address */
+ case OCD_RUN: /* Run Target from PC */
+ case OCD_RUN_ADDR: /* Run Target from Specified Address */
+ case OCD_STOP: /* Stop Target */
+ case OCD_RESET_RUN: /* Reset Target and Run */
+ case OCD_RESET: /* Reset Target and Halt */
+ case OCD_STEP: /* Single Step */
+ case OCD_WRITE_REGS: /* Write Register */
+ case OCD_WRITE_MEM: /* Write Memory */
+ case OCD_FILL_MEM: /* Fill Memory */
+ case OCD_MOVE_MEM: /* Move Memory */
+ case OCD_WRITE_INT_MEM: /* Write Internal Memory */
+ case OCD_JUMP: /* Jump to Subroutine */
+ case OCD_ERASE_FLASH: /* Erase flash memory */
+ case OCD_PROGRAM_FLASH: /* Write flash memory */
+ case OCD_EXIT_MON: /* Exit the flash programming monitor */
+ case OCD_ENTER_MON: /* Enter the flash programming monitor */
+ case OCD_LOG_FILE: /* Make Wigglers.dll save Wigglers.log */
+ case OCD_SET_CONNECTION: /* Set type of connection in Wigglers.dll */
+ len = 0;
+ break;
+ case OCD_GET_VERSION: /* Get Version */
+ len = 10;
+ break;
+ case OCD_GET_STATUS_MASK: /* Get Status Mask */
+ len = 1;
+ break;
+ case OCD_GET_CTRS: /* Get Error Counters */
+ case OCD_READ_REGS: /* Read Register */
+ case OCD_READ_MEM: /* Read Memory */
+ case OCD_READ_INT_MEM: /* Read Internal Memory */
+ len = 257;
+ break;
+ default:
+ error ("ocd_get_packet: unknown packet type 0x%x\n", ch);
+ }
+ }
+
+ if (len == 257) /* Byte stream? */
+ { /* Yes, byte streams contain the length */
+ ch = readchar (timeout);
+
+ if (ch < 0)
+ error ("ocd_get_packet (readchar): %d", ch);
+ *packet_ptr++ = ch;
+ checksum += ch;
+ len = ch;
+ if (len == 0)
+ len = 256;
+ }
+
+ while (len-- >= 0) /* Do rest of packet and checksum */
+ {
+ ch = readchar (timeout);
+
+ if (ch < 0)
+ error ("ocd_get_packet (readchar): %d", ch);
+ *packet_ptr++ = ch;
+ checksum += ch;
+ }
+
+ if (checksum != 0)
+ error ("ocd_get_packet: bad packet checksum");
+
+ if (cmd != -1 && cmd != packet[0])
+ error ("Response phase error. Got 0x%x, expected 0x%x", packet[0], cmd);
+
+ *lenp = packet_ptr - packet - 1; /* Subtract checksum byte */
+ return packet;
+}
+#endif
+
+/* Execute a simple (one-byte) command. Returns a pointer to the data
+ following the error code. */
+
+static unsigned char *
+ocd_do_command (cmd, statusp, lenp)
+ int cmd;
+ int *statusp;
+ int *lenp;
+{
+ unsigned char buf[100], *p;
+ int status, error_code;
+ char errbuf[100];
+
+ unsigned char logbuf[100];
+ int logpktlen;
+
+ buf[0] = cmd;
+ ocd_put_packet (buf, 1); /* Send command */
+ p = ocd_get_packet (*buf, lenp, remote_timeout);
+
+ if (*lenp < 3)
+ error ("Truncated response packet from OCD device");
+
+ status = p[1];
+ error_code = p[2];
+
+ if (error_code != 0)
+ {
+ sprintf (errbuf, "ocd_do_command (0x%x):", cmd);
+ ocd_error (errbuf, error_code);
+ }
+
+ if (status & OCD_FLAG_PWF)
+ error ("OCD device can't detect VCC at BDM interface.");
+ else if (status & OCD_FLAG_CABLE_DISC)
+ error ("BDM cable appears to be disconnected.");
+
+ *statusp = status;
+
+ logbuf[0] = OCD_LOG_FILE;
+ logbuf[1] = 3; /* close existing WIGGLERS.LOG */
+ ocd_put_packet (logbuf, 2);
+ ocd_get_packet (logbuf[0], &logpktlen, remote_timeout);
+
+ logbuf[0] = OCD_LOG_FILE;
+ logbuf[1] = 2; /* append to existing WIGGLERS.LOG */
+ ocd_put_packet (logbuf, 2);
+ ocd_get_packet (logbuf[0], &logpktlen, remote_timeout);
+
+ return p + 3;
+}
+
+void
+ocd_kill ()
+{
+ /* For some mysterious reason, wait_for_inferior calls kill instead of
+ mourn after it gets TARGET_WAITKIND_SIGNALLED. Work around it. */
+ if (kill_kludge)
+ {
+ kill_kludge = 0;
+ target_mourn_inferior ();
+ return;
+ }
+
+ /* Don't wait for it to die. I'm not really sure it matters whether
+ we do or not. */
+ target_mourn_inferior ();
+}
+
+void
+ocd_mourn ()
+{
+ unpush_target (current_ops);
+ generic_mourn_inferior ();
+}
+
+/* All we actually do is set the PC to the start address of exec_bfd, and start
+ the program at that point. */
+
+void
+ocd_create_inferior (exec_file, args, env)
+ char *exec_file;
+ char *args;
+ char **env;
+{
+ if (args && (*args != '\000'))
+ error ("Args are not supported by BDM.");
+
+ clear_proceed_status ();
+ proceed (bfd_get_start_address (exec_bfd), TARGET_SIGNAL_0, 0);
+}
+
+void
+ocd_load (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ generic_load (args, from_tty);
+
+ inferior_pid = 0;
+
+/* 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 ();
+}
+
+/* This should be defined for each target */
+/* But we want to be able to compile this file for some configurations
+ not yet supported fully */
+
+#define BDM_BREAKPOINT {0x0,0x0,0x0,0x0} /* For ppc 8xx */
+#if 0
+#define BDM_BREAKPOINT {0x4a,0xfa} /* BGND insn used for CPU32 */
+#endif
+
+/* BDM (at least on CPU32) uses a different breakpoint */
+
+int
+ocd_insert_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ static char break_insn[] = BDM_BREAKPOINT;
+ int val;
+
+ val = target_read_memory (addr, contents_cache, sizeof (break_insn));
+
+ if (val == 0)
+ val = target_write_memory (addr, break_insn, sizeof (break_insn));
+
+ return val;
+}
+
+int
+ocd_remove_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ static char break_insn[] = BDM_BREAKPOINT;
+ int val;
+
+ val = target_write_memory (addr, contents_cache, sizeof (break_insn));
+
+ return val;
+}
+
+static void
+bdm_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ error ("bdm command must be followed by `reset'");
+}
+
+static void
+bdm_reset_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ int status, pktlen;
+
+ if (!ocd_desc)
+ error ("Not connected to OCD device.");
+
+ ocd_do_command (OCD_RESET, &status, &pktlen);
+ dcache_flush (ocd_dcache);
+ registers_changed ();
+}
+
+static void
+bdm_restart_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ int status, pktlen;
+
+ if (!ocd_desc)
+ error ("Not connected to OCD device.");
+
+ ocd_do_command (OCD_RESET_RUN, &status, &pktlen);
+ last_run_status = status;
+ clear_proceed_status ();
+ wait_for_inferior ();
+ normal_stop ();
+}
+
+/* Temporary replacement for target_store_registers(). This prevents
+ generic_load from trying to set the PC. */
+
+static void
+noop_store_registers (regno)
+ int regno;
+{
+}
+
+static void
+bdm_update_flash_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ int status, pktlen;
+ struct cleanup *old_chain;
+ void (*store_registers_tmp) PARAMS ((int));
+
+ if (!ocd_desc)
+ error ("Not connected to OCD device.");
+
+ if (!args)
+ error ("Must specify file containing new OCD code.");
+
+/* old_chain = make_cleanup (flash_cleanup, 0);*/
+
+ ocd_do_command (OCD_ENTER_MON, &status, &pktlen);
+
+ ocd_do_command (OCD_ERASE_FLASH, &status, &pktlen);
+
+ write_mem_command = OCD_PROGRAM_FLASH;
+ store_registers_tmp = current_target.to_store_registers;
+ current_target.to_store_registers = noop_store_registers;
+
+ generic_load (args, from_tty);
+
+ current_target.to_store_registers = store_registers_tmp;
+ write_mem_command = OCD_WRITE_MEM;
+
+ ocd_do_command (OCD_EXIT_MON, &status, &pktlen);
+
+/* discard_cleanups (old_chain);*/
+}
+
+static void
+bdm_read_register_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ /* XXX repeat should go on to the next register */
+
+ if (!ocd_desc)
+ error ("Not connected to OCD device.");
+
+ if (!args)
+ error ("Must specify BDM register number.");
+
+}
+
+void
+_initialize_remote_ocd ()
+{
+ extern struct cmd_list_element *cmdlist;
+ static struct cmd_list_element *ocd_cmd_list = NULL;
+
+ add_show_from_set (add_set_cmd ("remotetimeout", no_class,
+ var_integer, (char *)&remote_timeout,
+ "Set timeout value for remote read.\n", &setlist),
+ &showlist);
+
+ add_prefix_cmd ("ocd", class_obscure, bdm_command, "", &ocd_cmd_list, "ocd ",
+ 0, &cmdlist);
+
+ add_cmd ("reset", class_obscure, bdm_reset_command, "", &ocd_cmd_list);
+ add_cmd ("restart", class_obscure, bdm_restart_command, "", &ocd_cmd_list);
+ add_cmd ("update-flash", class_obscure, bdm_update_flash_command, "", &ocd_cmd_list);
+ /* add_cmd ("read-register", class_obscure, bdm_read_register_command, "", &ocd_cmd_list);*/
+}