| Index: sim/rl78/gdb-if.c
|
| diff --git a/sim/rl78/gdb-if.c b/sim/rl78/gdb-if.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e2a5ea0985ee3fe2a5643c98bde63cc45eb2bb46
|
| --- /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 GDB_SIGNAL_ILL;
|
| +
|
| + case 5:
|
| + return GDB_SIGNAL_TRAP;
|
| +
|
| + case 10:
|
| + return GDB_SIGNAL_BUS;
|
| +
|
| + case 11:
|
| + return GDB_SIGNAL_SEGV;
|
| +
|
| + case 24:
|
| + return GDB_SIGNAL_XCPU;
|
| + break;
|
| +
|
| + case 2:
|
| + return GDB_SIGNAL_INT;
|
| +
|
| + case 8:
|
| + return GDB_SIGNAL_FPE;
|
| + break;
|
| +
|
| + case 6:
|
| + return GDB_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 = GDB_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 = GDB_SIGNAL_INT;
|
| + break;
|
| + }
|
| +
|
| + if (hw_breakpoints[pc >> 3]
|
| + && (hw_breakpoints[pc >> 3] & (1 << (pc & 0x7))))
|
| + {
|
| + reason = sim_stopped;
|
| + siggnal = GDB_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;
|
| +}
|
|
|