diff options
Diffstat (limited to 'sim/rl78/gdb-if.c')
-rw-r--r-- | sim/rl78/gdb-if.c | 573 |
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; +} |