aboutsummaryrefslogtreecommitdiff
path: root/sim/rl78/gdb-if.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/rl78/gdb-if.c')
-rw-r--r--sim/rl78/gdb-if.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/sim/rl78/gdb-if.c b/sim/rl78/gdb-if.c
new file mode 100644
index 0000000..e7c1277
--- /dev/null
+++ b/sim/rl78/gdb-if.c
@@ -0,0 +1,573 @@
+/* gdb-if.c -- sim interface to GDB.
+
+Copyright (C) 2011-2012 Free Software Foundation, Inc.
+Contributed by Red Hat, Inc.
+
+This file is part of the GNU simulators.
+
+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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "ansidecl.h"
+#include "gdb/callback.h"
+#include "gdb/remote-sim.h"
+#include "gdb/signals.h"
+#include "gdb/sim-rl78.h"
+
+#include "cpu.h"
+#include "mem.h"
+#include "load.h"
+#include "trace.h"
+
+/* Ideally, we'd wrap up all the minisim's data structures in an
+ object and pass that around. However, neither GDB nor run needs
+ that ability.
+
+ So we just have one instance, that lives in global variables, and
+ each time we open it, we re-initialize it. */
+
+struct sim_state
+{
+ const char *message;
+};
+
+static struct sim_state the_minisim = {
+ "This is the sole rl78 minisim instance."
+};
+
+static int open;
+
+static unsigned char hw_breakpoints[MEM_SIZE/8];
+
+static struct host_callback_struct *host_callbacks;
+
+/* Open an instance of the sim. For this sim, only one instance
+ is permitted. If sim_open() is called multiple times, the sim
+ will be reset. */
+
+SIM_DESC
+sim_open (SIM_OPEN_KIND kind,
+ struct host_callback_struct *callback,
+ struct bfd *abfd, char **argv)
+{
+ if (open)
+ fprintf (stderr, "rl78 minisim: re-opened sim\n");
+
+ /* The 'run' interface doesn't use this function, so we don't care
+ about KIND; it's always SIM_OPEN_DEBUG. */
+ if (kind != SIM_OPEN_DEBUG)
+ fprintf (stderr, "rl78 minisim: sim_open KIND != SIM_OPEN_DEBUG: %d\n",
+ kind);
+
+ /* We use this for the load command. Perhaps someday, it'll be used
+ for syscalls too. */
+ host_callbacks = callback;
+
+ /* We don't expect any command-line arguments. */
+
+ init_cpu ();
+ trace = 0;
+
+ sim_disasm_init (abfd);
+ open = 1;
+ return &the_minisim;
+}
+
+/* Verify the sim descriptor. Just print a message if the descriptor
+ doesn't match. Nothing bad will happen if the descriptor doesn't
+ match because all of the state is global. But if it doesn't
+ match, that means there's a problem with the caller. */
+
+static void
+check_desc (SIM_DESC sd)
+{
+ if (sd != &the_minisim)
+ fprintf (stderr, "rl78 minisim: desc != &the_minisim\n");
+}
+
+/* Close the sim. */
+
+void
+sim_close (SIM_DESC sd, int quitting)
+{
+ check_desc (sd);
+
+ /* Not much to do. At least free up our memory. */
+ init_mem ();
+
+ open = 0;
+}
+
+/* Open the program to run; print a message if the program cannot
+ be opened. */
+
+static bfd *
+open_objfile (const char *filename)
+{
+ bfd *prog = bfd_openr (filename, 0);
+
+ if (!prog)
+ {
+ fprintf (stderr, "Can't read %s\n", filename);
+ return 0;
+ }
+
+ if (!bfd_check_format (prog, bfd_object))
+ {
+ fprintf (stderr, "%s not a rl78 program\n", filename);
+ return 0;
+ }
+
+ return prog;
+}
+
+/* Load a program. */
+
+SIM_RC
+sim_load (SIM_DESC sd, char *prog, struct bfd *abfd, int from_tty)
+{
+ check_desc (sd);
+
+ if (!abfd)
+ abfd = open_objfile (prog);
+ if (!abfd)
+ return SIM_RC_FAIL;
+
+ rl78_load (abfd, host_callbacks, "sim");
+
+ return SIM_RC_OK;
+}
+
+/* Create inferior. */
+
+SIM_RC
+sim_create_inferior (SIM_DESC sd, struct bfd *abfd, char **argv, char **env)
+{
+ check_desc (sd);
+
+ if (abfd)
+ rl78_load (abfd, 0, "sim");
+
+ return SIM_RC_OK;
+}
+
+/* Read memory. */
+
+int
+sim_read (SIM_DESC sd, SIM_ADDR mem, unsigned char *buf, int length)
+{
+ check_desc (sd);
+
+ if (mem >= MEM_SIZE)
+ return 0;
+ else if (mem + length > MEM_SIZE)
+ length = MEM_SIZE - mem;
+
+ mem_get_blk (mem, buf, length);
+ return length;
+}
+
+/* Write memory. */
+
+int
+sim_write (SIM_DESC sd, SIM_ADDR mem, const unsigned char *buf, int length)
+{
+ check_desc (sd);
+
+ if (mem >= MEM_SIZE)
+ return 0;
+ else if (mem + length > MEM_SIZE)
+ length = MEM_SIZE - mem;
+
+ mem_put_blk (mem, buf, length);
+ return length;
+}
+
+/* Read the LENGTH bytes at BUF as an little-endian value. */
+
+static SI
+get_le (unsigned char *buf, int length)
+{
+ SI acc = 0;
+
+ while (--length >= 0)
+ acc = (acc << 8) + buf[length];
+
+ return acc;
+}
+
+/* Store VAL as a little-endian value in the LENGTH bytes at BUF. */
+
+static void
+put_le (unsigned char *buf, int length, SI val)
+{
+ int i;
+
+ for (i = 0; i < length; i++)
+ {
+ buf[i] = val & 0xff;
+ val >>= 8;
+ }
+}
+
+/* Verify that REGNO is in the proper range. Return 0 if not and
+ something non-zero if so. */
+
+static int
+check_regno (enum sim_rl78_regnum regno)
+{
+ return 0 <= regno && regno < sim_rl78_num_regs;
+}
+
+/* Return the size of the register REGNO. */
+
+static size_t
+reg_size (enum sim_rl78_regnum regno)
+{
+ size_t size;
+
+ if (regno == sim_rl78_pc_regnum)
+ size = 4;
+ else
+ size = 1;
+
+ return size;
+}
+
+/* Return the register address associated with the register specified by
+ REGNO. */
+
+static unsigned long
+reg_addr (enum sim_rl78_regnum regno)
+{
+ if (sim_rl78_bank0_r0_regnum <= regno
+ && regno <= sim_rl78_bank0_r7_regnum)
+ return 0xffef8 + (regno - sim_rl78_bank0_r0_regnum);
+ else if (sim_rl78_bank1_r0_regnum <= regno
+ && regno <= sim_rl78_bank1_r7_regnum)
+ return 0xffef0 + (regno - sim_rl78_bank1_r0_regnum);
+ else if (sim_rl78_bank2_r0_regnum <= regno
+ && regno <= sim_rl78_bank2_r7_regnum)
+ return 0xffee8 + (regno - sim_rl78_bank2_r0_regnum);
+ else if (sim_rl78_bank3_r0_regnum <= regno
+ && regno <= sim_rl78_bank3_r7_regnum)
+ return 0xffee0 + (regno - sim_rl78_bank3_r0_regnum);
+ else if (regno == sim_rl78_psw_regnum)
+ return 0xffffa;
+ else if (regno == sim_rl78_es_regnum)
+ return 0xffffd;
+ else if (regno == sim_rl78_cs_regnum)
+ return 0xffffc;
+ /* Note: We can't handle PC here because it's not memory mapped. */
+ else if (regno == sim_rl78_spl_regnum)
+ return 0xffff8;
+ else if (regno == sim_rl78_sph_regnum)
+ return 0xffff9;
+ else if (regno == sim_rl78_pmc_regnum)
+ return 0xffffe;
+ else if (regno == sim_rl78_mem_regnum)
+ return 0xfffff;
+
+ return 0;
+}
+
+/* Fetch the contents of the register specified by REGNO, placing the
+ contents in BUF. The length LENGTH must match the sim's internal
+ notion of the register's size. */
+
+int
+sim_fetch_register (SIM_DESC sd, int regno, unsigned char *buf, int length)
+{
+ size_t size;
+ SI val;
+
+ check_desc (sd);
+
+ if (!check_regno (regno))
+ return 0;
+
+ size = reg_size (regno);
+
+ if (length != size)
+ return 0;
+
+ if (regno == sim_rl78_pc_regnum)
+ val = pc;
+ else
+ val = memory[reg_addr (regno)];
+
+ put_le (buf, length, val);
+
+ return size;
+}
+
+/* Store the value stored in BUF to the register REGNO. The length
+ LENGTH must match the sim's internal notion of the register size. */
+
+int
+sim_store_register (SIM_DESC sd, int regno, unsigned char *buf, int length)
+{
+ size_t size;
+ SI val;
+
+ check_desc (sd);
+
+ if (!check_regno (regno))
+ return -1;
+
+ size = reg_size (regno);
+
+ if (length != size)
+ return -1;
+
+ val = get_le (buf, length);
+
+ if (regno == sim_rl78_pc_regnum)
+ pc = val;
+ else
+ memory[reg_addr (regno)] = val;
+ return size;
+}
+
+/* Print out message associated with "info target". */
+
+void
+sim_info (SIM_DESC sd, int verbose)
+{
+ check_desc (sd);
+
+ printf ("The rl78 minisim doesn't collect any statistics.\n");
+}
+
+static volatile int stop;
+static enum sim_stop reason;
+int siggnal;
+
+
+/* Given a signal number used by the rl78 bsp (that is, newlib),
+ return the corresponding signal numbers. */
+
+int
+rl78_signal_to_target (int sig)
+{
+ switch (sig)
+ {
+ case 4:
+ return TARGET_SIGNAL_ILL;
+
+ case 5:
+ return TARGET_SIGNAL_TRAP;
+
+ case 10:
+ return TARGET_SIGNAL_BUS;
+
+ case 11:
+ return TARGET_SIGNAL_SEGV;
+
+ case 24:
+ return TARGET_SIGNAL_XCPU;
+ break;
+
+ case 2:
+ return TARGET_SIGNAL_INT;
+
+ case 8:
+ return TARGET_SIGNAL_FPE;
+ break;
+
+ case 6:
+ return TARGET_SIGNAL_ABRT;
+ }
+
+ return 0;
+}
+
+
+/* Take a step return code RC and set up the variables consulted by
+ sim_stop_reason appropriately. */
+
+void
+handle_step (int rc)
+{
+ if (RL78_STEPPED (rc) || RL78_HIT_BREAK (rc))
+ {
+ reason = sim_stopped;
+ siggnal = TARGET_SIGNAL_TRAP;
+ }
+ else if (RL78_STOPPED (rc))
+ {
+ reason = sim_stopped;
+ siggnal = rl78_signal_to_target (RL78_STOP_SIG (rc));
+ }
+ else
+ {
+ assert (RL78_EXITED (rc));
+ reason = sim_exited;
+ siggnal = RL78_EXIT_STATUS (rc);
+ }
+}
+
+
+/* Resume execution after a stop. */
+
+void
+sim_resume (SIM_DESC sd, int step, int sig_to_deliver)
+{
+ int rc;
+
+ check_desc (sd);
+
+ if (sig_to_deliver != 0)
+ {
+ fprintf (stderr,
+ "Warning: the rl78 minisim does not implement "
+ "signal delivery yet.\n" "Resuming with no signal.\n");
+ }
+
+ /* We don't clear 'stop' here, because then we would miss
+ interrupts that arrived on the way here. Instead, we clear
+ the flag in sim_stop_reason, after GDB has disabled the
+ interrupt signal handler. */
+ for (;;)
+ {
+ if (stop)
+ {
+ stop = 0;
+ reason = sim_stopped;
+ siggnal = TARGET_SIGNAL_INT;
+ break;
+ }
+
+ if (hw_breakpoints[pc >> 3]
+ && (hw_breakpoints[pc >> 3] & (1 << (pc & 0x7))))
+ {
+ reason = sim_stopped;
+ siggnal = TARGET_SIGNAL_TRAP;
+ break;
+ }
+ rc = setjmp (decode_jmp_buf);
+ if (rc == 0)
+ rc = decode_opcode ();
+
+ if (!RL78_STEPPED (rc) || step)
+ {
+ handle_step (rc);
+ break;
+ }
+ }
+}
+
+/* Stop the sim. */
+
+int
+sim_stop (SIM_DESC sd)
+{
+ stop = 1;
+
+ return 1;
+}
+
+/* Fetch the stop reason and signal. */
+
+void
+sim_stop_reason (SIM_DESC sd, enum sim_stop *reason_p, int *sigrc_p)
+{
+ check_desc (sd);
+
+ *reason_p = reason;
+ *sigrc_p = siggnal;
+}
+
+/* Execute the sim-specific command associated with GDB's "sim ..."
+ command. */
+
+void
+sim_do_command (SIM_DESC sd, char *cmd)
+{
+ char *args;
+
+ check_desc (sd);
+
+ if (cmd == NULL)
+ {
+ cmd = "";
+ args = "";
+ }
+ else
+ {
+ char *p = cmd;
+
+ /* Skip leading whitespace. */
+ while (isspace (*p))
+ p++;
+
+ /* Find the extent of the command word. */
+ for (p = cmd; *p; p++)
+ if (isspace (*p))
+ break;
+
+ /* Null-terminate the command word, and record the start of any
+ further arguments. */
+ if (*p)
+ {
+ *p = '\0';
+ args = p + 1;
+ while (isspace (*args))
+ args++;
+ }
+ else
+ args = p;
+ }
+
+ if (strcmp (cmd, "trace") == 0)
+ {
+ if (strcmp (args, "on") == 0)
+ trace = 1;
+ else if (strcmp (args, "off") == 0)
+ trace = 0;
+ else
+ printf ("The 'sim trace' command expects 'on' or 'off' "
+ "as an argument.\n");
+ }
+ else if (strcmp (cmd, "verbose") == 0)
+ {
+ if (strcmp (args, "on") == 0)
+ verbose = 1;
+ else if (strcmp (args, "noisy") == 0)
+ verbose = 2;
+ else if (strcmp (args, "off") == 0)
+ verbose = 0;
+ else
+ printf ("The 'sim verbose' command expects 'on', 'noisy', or 'off'"
+ " as an argument.\n");
+ }
+ else
+ printf ("The 'sim' command expects either 'trace' or 'verbose'"
+ " as a subcommand.\n");
+}
+
+/* Stub for command completion. */
+
+char **
+sim_complete_command (SIM_DESC sd, char *text, char *word)
+{
+ return NULL;
+}