594 lines
12 KiB
C
594 lines
12 KiB
C
/* gdb-if.c -- sim interface to GDB.
|
|
|
|
Copyright (C) 2011-2022 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 <string.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "ansidecl.h"
|
|
#include "libiberty.h"
|
|
#include "sim/callback.h"
|
|
#include "sim/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 is_open;
|
|
|
|
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 * const *argv)
|
|
{
|
|
if (is_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);
|
|
is_open = 1;
|
|
|
|
while (argv != NULL && *argv != NULL)
|
|
{
|
|
if (strcmp (*argv, "g10") == 0 || strcmp (*argv, "-Mg10") == 0)
|
|
{
|
|
fprintf (stderr, "rl78 g10 support enabled.\n");
|
|
rl78_g10_mode = 1;
|
|
g13_multiply = 0;
|
|
g14_multiply = 0;
|
|
mem_set_mirror (0, 0xf8000, 4096);
|
|
break;
|
|
}
|
|
if (strcmp (*argv, "g13") == 0 || strcmp (*argv, "-Mg13") == 0)
|
|
{
|
|
fprintf (stderr, "rl78 g13 support enabled.\n");
|
|
rl78_g10_mode = 0;
|
|
g13_multiply = 1;
|
|
g14_multiply = 0;
|
|
break;
|
|
}
|
|
if (strcmp (*argv, "g14") == 0 || strcmp (*argv, "-Mg14") == 0)
|
|
{
|
|
fprintf (stderr, "rl78 g14 support enabled.\n");
|
|
rl78_g10_mode = 0;
|
|
g13_multiply = 0;
|
|
g14_multiply = 1;
|
|
break;
|
|
}
|
|
argv++;
|
|
}
|
|
|
|
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 ();
|
|
|
|
is_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, const 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 * const *argv, char * const *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;
|
|
|
|
/* The rl78 program counter is 20 bits wide. Ensure that GDB
|
|
hasn't picked up any stray bits. This has occurred when performing
|
|
a GDB "return" command in which the return address is obtained
|
|
from a 32-bit container on the stack. */
|
|
assert ((pc & ~0x0fffff) == 0);
|
|
}
|
|
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. */
|
|
|
|
static 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. */
|
|
|
|
static 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;
|
|
}
|
|
|
|
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, 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, "noisy") == 0)
|
|
verbose = 2;
|
|
else if (strcmp (arg, "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");
|
|
|
|
freeargv (argv);
|
|
}
|
|
|
|
/* Stub for command completion. */
|
|
|
|
char **
|
|
sim_complete_command (SIM_DESC sd, const char *text, const char *word)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
sim_memory_map (SIM_DESC sd)
|
|
{
|
|
return NULL;
|
|
}
|