| /* gdb.c --- sim interface to GDB. |
| |
| Copyright (C) 2005-2021 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/>. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include <stdio.h> |
| #include <assert.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #include "ansidecl.h" |
| #include "libiberty.h" |
| #include "sim/callback.h" |
| #include "sim/sim.h" |
| #include "gdb/signals.h" |
| #include "gdb/sim-m32c.h" |
| |
| #include "cpu.h" |
| #include "mem.h" |
| #include "load.h" |
| #include "syscalls.h" |
| #ifdef TIMER_A |
| #include "timer_a.h" |
| #endif |
| |
| /* I don't want to wrap up all the minisim's data structures in an |
| object and pass that around. That'd be a big change, and 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 m32c minisim instance. See libsim.a's global variables." |
| }; |
| |
| static int is_open; |
| |
| SIM_DESC |
| sim_open (SIM_OPEN_KIND kind, |
| struct host_callback_struct *callback, |
| struct bfd *abfd, char * const *argv) |
| { |
| setbuf (stdout, 0); |
| if (is_open) |
| fprintf (stderr, "m32c 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, "m32c minisim: sim_open KIND != SIM_OPEN_DEBUG: %d\n", |
| kind); |
| |
| if (abfd) |
| m32c_set_mach (bfd_get_mach (abfd)); |
| |
| /* We can use ABFD, if non-NULL to select the appropriate |
| architecture. But we only support the r8c right now. */ |
| |
| set_callbacks (callback); |
| |
| /* We don't expect any command-line arguments. */ |
| |
| init_mem (); |
| init_regs (); |
| |
| is_open = 1; |
| return &the_minisim; |
| } |
| |
| static void |
| check_desc (SIM_DESC sd) |
| { |
| if (sd != &the_minisim) |
| fprintf (stderr, "m32c minisim: desc != &the_minisim\n"); |
| } |
| |
| void |
| sim_close (SIM_DESC sd, int quitting) |
| { |
| check_desc (sd); |
| |
| /* Not much to do. At least free up our memory. */ |
| init_mem (); |
| |
| is_open = 0; |
| } |
| |
| 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 m32c program\n", filename); |
| return 0; |
| } |
| |
| return prog; |
| } |
| |
| |
| SIM_RC |
| sim_load (SIM_DESC sd, const char *prog, struct bfd * abfd, int from_tty) |
| { |
| check_desc (sd); |
| |
| if (!abfd) |
| abfd = open_objfile (prog); |
| if (!abfd) |
| return SIM_RC_FAIL; |
| |
| m32c_load (abfd); |
| |
| return SIM_RC_OK; |
| } |
| |
| SIM_RC |
| sim_create_inferior (SIM_DESC sd, struct bfd * abfd, |
| char * const *argv, char * const *env) |
| { |
| check_desc (sd); |
| |
| if (abfd) |
| m32c_load (abfd); |
| |
| return SIM_RC_OK; |
| } |
| |
| int |
| sim_read (SIM_DESC sd, SIM_ADDR mem, unsigned char *buf, int length) |
| { |
| check_desc (sd); |
| |
| if (mem == 0) |
| return 0; |
| |
| mem_get_blk ((int) mem, buf, length); |
| |
| return length; |
| } |
| |
| int |
| sim_write (SIM_DESC sd, SIM_ADDR mem, const unsigned char *buf, int length) |
| { |
| check_desc (sd); |
| |
| mem_put_blk ((int) mem, buf, length); |
| |
| return length; |
| } |
| |
| |
| /* Read the LENGTH bytes at BUF as an little-endian value. */ |
| static DI |
| get_le (unsigned char *buf, int length) |
| { |
| DI 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, DI val) |
| { |
| int i; |
| |
| for (i = 0; i < length; i++) |
| { |
| buf[i] = val & 0xff; |
| val >>= 8; |
| } |
| } |
| |
| static int |
| check_regno (enum m32c_sim_reg regno) |
| { |
| return 0 <= regno && regno < m32c_sim_reg_num_regs; |
| } |
| |
| static size_t |
| mask_size (int addr_mask) |
| { |
| switch (addr_mask) |
| { |
| case 0xffff: |
| return 2; |
| case 0xfffff: |
| case 0xffffff: |
| return 3; |
| default: |
| fprintf (stderr, |
| "m32c minisim: addr_mask_size: unexpected mask 0x%x\n", |
| addr_mask); |
| return sizeof (addr_mask); |
| } |
| } |
| |
| static size_t |
| reg_size (enum m32c_sim_reg regno) |
| { |
| switch (regno) |
| { |
| case m32c_sim_reg_r0_bank0: |
| case m32c_sim_reg_r1_bank0: |
| case m32c_sim_reg_r2_bank0: |
| case m32c_sim_reg_r3_bank0: |
| case m32c_sim_reg_r0_bank1: |
| case m32c_sim_reg_r1_bank1: |
| case m32c_sim_reg_r2_bank1: |
| case m32c_sim_reg_r3_bank1: |
| case m32c_sim_reg_flg: |
| case m32c_sim_reg_svf: |
| return 2; |
| |
| case m32c_sim_reg_a0_bank0: |
| case m32c_sim_reg_a1_bank0: |
| case m32c_sim_reg_fb_bank0: |
| case m32c_sim_reg_sb_bank0: |
| case m32c_sim_reg_a0_bank1: |
| case m32c_sim_reg_a1_bank1: |
| case m32c_sim_reg_fb_bank1: |
| case m32c_sim_reg_sb_bank1: |
| case m32c_sim_reg_usp: |
| case m32c_sim_reg_isp: |
| return mask_size (addr_mask); |
| |
| case m32c_sim_reg_pc: |
| case m32c_sim_reg_intb: |
| case m32c_sim_reg_svp: |
| case m32c_sim_reg_vct: |
| return mask_size (membus_mask); |
| |
| case m32c_sim_reg_dmd0: |
| case m32c_sim_reg_dmd1: |
| return 1; |
| |
| case m32c_sim_reg_dct0: |
| case m32c_sim_reg_dct1: |
| case m32c_sim_reg_drc0: |
| case m32c_sim_reg_drc1: |
| return 2; |
| |
| case m32c_sim_reg_dma0: |
| case m32c_sim_reg_dma1: |
| case m32c_sim_reg_dsa0: |
| case m32c_sim_reg_dsa1: |
| case m32c_sim_reg_dra0: |
| case m32c_sim_reg_dra1: |
| return 3; |
| |
| default: |
| fprintf (stderr, "m32c minisim: unrecognized register number: %d\n", |
| regno); |
| return -1; |
| } |
| } |
| |
| int |
| sim_fetch_register (SIM_DESC sd, int regno, unsigned char *buf, int length) |
| { |
| size_t size; |
| |
| check_desc (sd); |
| |
| if (!check_regno (regno)) |
| return 0; |
| |
| size = reg_size (regno); |
| if (length == size) |
| { |
| DI val; |
| |
| switch (regno) |
| { |
| case m32c_sim_reg_r0_bank0: |
| val = regs.r[0].r_r0; |
| break; |
| case m32c_sim_reg_r1_bank0: |
| val = regs.r[0].r_r1; |
| break; |
| case m32c_sim_reg_r2_bank0: |
| val = regs.r[0].r_r2; |
| break; |
| case m32c_sim_reg_r3_bank0: |
| val = regs.r[0].r_r3; |
| break; |
| case m32c_sim_reg_a0_bank0: |
| val = regs.r[0].r_a0; |
| break; |
| case m32c_sim_reg_a1_bank0: |
| val = regs.r[0].r_a1; |
| break; |
| case m32c_sim_reg_fb_bank0: |
| val = regs.r[0].r_fb; |
| break; |
| case m32c_sim_reg_sb_bank0: |
| val = regs.r[0].r_sb; |
| break; |
| case m32c_sim_reg_r0_bank1: |
| val = regs.r[1].r_r0; |
| break; |
| case m32c_sim_reg_r1_bank1: |
| val = regs.r[1].r_r1; |
| break; |
| case m32c_sim_reg_r2_bank1: |
| val = regs.r[1].r_r2; |
| break; |
| case m32c_sim_reg_r3_bank1: |
| val = regs.r[1].r_r3; |
| break; |
| case m32c_sim_reg_a0_bank1: |
| val = regs.r[1].r_a0; |
| break; |
| case m32c_sim_reg_a1_bank1: |
| val = regs.r[1].r_a1; |
| break; |
| case m32c_sim_reg_fb_bank1: |
| val = regs.r[1].r_fb; |
| break; |
| case m32c_sim_reg_sb_bank1: |
| val = regs.r[1].r_sb; |
| break; |
| |
| case m32c_sim_reg_usp: |
| val = regs.r_usp; |
| break; |
| case m32c_sim_reg_isp: |
| val = regs.r_isp; |
| break; |
| case m32c_sim_reg_pc: |
| val = regs.r_pc; |
| break; |
| case m32c_sim_reg_intb: |
| val = regs.r_intbl * 65536 + regs.r_intbl; |
| break; |
| case m32c_sim_reg_flg: |
| val = regs.r_flags; |
| break; |
| |
| /* These registers aren't implemented by the minisim. */ |
| case m32c_sim_reg_svf: |
| case m32c_sim_reg_svp: |
| case m32c_sim_reg_vct: |
| case m32c_sim_reg_dmd0: |
| case m32c_sim_reg_dmd1: |
| case m32c_sim_reg_dct0: |
| case m32c_sim_reg_dct1: |
| case m32c_sim_reg_drc0: |
| case m32c_sim_reg_drc1: |
| case m32c_sim_reg_dma0: |
| case m32c_sim_reg_dma1: |
| case m32c_sim_reg_dsa0: |
| case m32c_sim_reg_dsa1: |
| case m32c_sim_reg_dra0: |
| case m32c_sim_reg_dra1: |
| return 0; |
| |
| default: |
| fprintf (stderr, "m32c minisim: unrecognized register number: %d\n", |
| regno); |
| return -1; |
| } |
| |
| put_le (buf, length, val); |
| } |
| |
| return size; |
| } |
| |
| int |
| sim_store_register (SIM_DESC sd, int regno, unsigned char *buf, int length) |
| { |
| size_t size; |
| |
| check_desc (sd); |
| |
| if (!check_regno (regno)) |
| return -1; |
| |
| size = reg_size (regno); |
| |
| if (length == size) |
| { |
| DI val = get_le (buf, length); |
| |
| switch (regno) |
| { |
| case m32c_sim_reg_r0_bank0: |
| regs.r[0].r_r0 = val & 0xffff; |
| break; |
| case m32c_sim_reg_r1_bank0: |
| regs.r[0].r_r1 = val & 0xffff; |
| break; |
| case m32c_sim_reg_r2_bank0: |
| regs.r[0].r_r2 = val & 0xffff; |
| break; |
| case m32c_sim_reg_r3_bank0: |
| regs.r[0].r_r3 = val & 0xffff; |
| break; |
| case m32c_sim_reg_a0_bank0: |
| regs.r[0].r_a0 = val & addr_mask; |
| break; |
| case m32c_sim_reg_a1_bank0: |
| regs.r[0].r_a1 = val & addr_mask; |
| break; |
| case m32c_sim_reg_fb_bank0: |
| regs.r[0].r_fb = val & addr_mask; |
| break; |
| case m32c_sim_reg_sb_bank0: |
| regs.r[0].r_sb = val & addr_mask; |
| break; |
| case m32c_sim_reg_r0_bank1: |
| regs.r[1].r_r0 = val & 0xffff; |
| break; |
| case m32c_sim_reg_r1_bank1: |
| regs.r[1].r_r1 = val & 0xffff; |
| break; |
| case m32c_sim_reg_r2_bank1: |
| regs.r[1].r_r2 = val & 0xffff; |
| break; |
| case m32c_sim_reg_r3_bank1: |
| regs.r[1].r_r3 = val & 0xffff; |
| break; |
| case m32c_sim_reg_a0_bank1: |
| regs.r[1].r_a0 = val & addr_mask; |
| break; |
| case m32c_sim_reg_a1_bank1: |
| regs.r[1].r_a1 = val & addr_mask; |
| break; |
| case m32c_sim_reg_fb_bank1: |
| regs.r[1].r_fb = val & addr_mask; |
| break; |
| case m32c_sim_reg_sb_bank1: |
| regs.r[1].r_sb = val & addr_mask; |
| break; |
| |
| case m32c_sim_reg_usp: |
| regs.r_usp = val & addr_mask; |
| break; |
| case m32c_sim_reg_isp: |
| regs.r_isp = val & addr_mask; |
| break; |
| case m32c_sim_reg_pc: |
| regs.r_pc = val & membus_mask; |
| break; |
| case m32c_sim_reg_intb: |
| regs.r_intbl = (val & membus_mask) & 0xffff; |
| regs.r_intbh = (val & membus_mask) >> 16; |
| break; |
| case m32c_sim_reg_flg: |
| regs.r_flags = val & 0xffff; |
| break; |
| |
| /* These registers aren't implemented by the minisim. */ |
| case m32c_sim_reg_svf: |
| case m32c_sim_reg_svp: |
| case m32c_sim_reg_vct: |
| case m32c_sim_reg_dmd0: |
| case m32c_sim_reg_dmd1: |
| case m32c_sim_reg_dct0: |
| case m32c_sim_reg_dct1: |
| case m32c_sim_reg_drc0: |
| case m32c_sim_reg_drc1: |
| case m32c_sim_reg_dma0: |
| case m32c_sim_reg_dma1: |
| case m32c_sim_reg_dsa0: |
| case m32c_sim_reg_dsa1: |
| case m32c_sim_reg_dra0: |
| case m32c_sim_reg_dra1: |
| return 0; |
| |
| default: |
| fprintf (stderr, "m32c minisim: unrecognized register number: %d\n", |
| regno); |
| return 0; |
| } |
| } |
| |
| return size; |
| } |
| |
| static volatile int stop; |
| static enum sim_stop reason; |
| static int siggnal; |
| |
| |
| /* Given a signal number used by the M32C bsp (that is, newlib), |
| return a target signal number used by GDB. */ |
| static int |
| m32c_signal_to_target (int m32c) |
| { |
| switch (m32c) |
| { |
| 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; |
| |
| case 2: |
| return GDB_SIGNAL_INT; |
| |
| case 8: |
| return GDB_SIGNAL_FPE; |
| |
| 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. */ |
| static void |
| handle_step (int rc) |
| { |
| if (M32C_STEPPED (rc) || M32C_HIT_BREAK (rc)) |
| { |
| reason = sim_stopped; |
| siggnal = GDB_SIGNAL_TRAP; |
| } |
| else if (M32C_STOPPED (rc)) |
| { |
| reason = sim_stopped; |
| siggnal = m32c_signal_to_target (M32C_STOP_SIG (rc)); |
| } |
| else |
| { |
| assert (M32C_EXITED (rc)); |
| reason = sim_exited; |
| siggnal = M32C_EXIT_STATUS (rc); |
| } |
| } |
| |
| |
| void |
| sim_resume (SIM_DESC sd, int step, int sig_to_deliver) |
| { |
| check_desc (sd); |
| |
| if (sig_to_deliver != 0) |
| { |
| fprintf (stderr, |
| "Warning: the m32c minisim does not implement " |
| "signal delivery yet.\n" "Resuming with no signal.\n"); |
| } |
| |
| if (step) |
| { |
| handle_step (decode_opcode ()); |
| #ifdef TIMER_A |
| update_timer_a (); |
| #endif |
| } |
| else |
| { |
| /* 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 (;;) |
| { |
| int rc; |
| |
| if (stop) |
| { |
| stop = 0; |
| reason = sim_stopped; |
| siggnal = GDB_SIGNAL_INT; |
| break; |
| } |
| |
| rc = decode_opcode (); |
| #ifdef TIMER_A |
| update_timer_a (); |
| #endif |
| |
| if (!M32C_STEPPED (rc)) |
| { |
| handle_step (rc); |
| break; |
| } |
| } |
| } |
| m32c_sim_restore_console (); |
| } |
| |
| int |
| sim_stop (SIM_DESC sd) |
| { |
| stop = 1; |
| |
| return 1; |
| } |
| |
| void |
| sim_stop_reason (SIM_DESC sd, enum sim_stop *reason_p, int *sigrc_p) |
| { |
| check_desc (sd); |
| |
| *reason_p = reason; |
| *sigrc_p = siggnal; |
| } |
| |
| void |
| sim_do_command (SIM_DESC sd, const char *cmd) |
| { |
| const char *arg; |
| char **argv = buildargv (cmd); |
| |
| check_desc (sd); |
| |
| cmd = arg = ""; |
| if (argv != NULL) |
| { |
| if (argv[0] != NULL) |
| cmd = argv[0]; |
| if (argv[1] != NULL) |
| arg = argv[1]; |
| } |
| |
| if (strcmp (cmd, "trace") == 0) |
| { |
| if (strcmp (arg, "on") == 0) |
| trace = 1; |
| else if (strcmp (arg, "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 (arg, "on") == 0) |
| verbose = 1; |
| else if (strcmp (arg, "off") == 0) |
| verbose = 0; |
| else |
| printf ("The 'sim verbose' command expects 'on' or 'off'" |
| " as an argument.\n"); |
| } |
| else |
| printf ("The 'sim' command expects either 'trace' or 'verbose'" |
| " as a subcommand.\n"); |
| |
| freeargv (argv); |
| } |
| |
| char ** |
| sim_complete_command (SIM_DESC sd, const char *text, const char *word) |
| { |
| return NULL; |
| } |
| |
| char * |
| sim_memory_map (SIM_DESC sd) |
| { |
| return NULL; |
| } |
| |
| void |
| sim_info (SIM_DESC sd, int verbose) |
| { |
| printf ("The m32c minisim doesn't collect any statistics.\n"); |
| } |