/* gdb.c --- sim interface to GDB.

Copyright (C) 2005-2016 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 <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "ansidecl.h"
#include "gdb/callback.h"
#include "gdb/remote-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 open;

SIM_DESC
sim_open (SIM_OPEN_KIND kind,
	  struct host_callback_struct *callback,
	  struct bfd *abfd, char * const *argv)
{
  setbuf (stdout, 0);
  if (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 ();

  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 ();

  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 *args;
  char *p = strdup (cmd);

  check_desc (sd);

  /* 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, "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");

  free (p);
}

char **
sim_complete_command (SIM_DESC sd, const char *text, const char *word)
{
  return NULL;
}

void
sim_info (SIM_DESC sd, int verbose)
{
  printf ("The m32c minisim doesn't collect any statistics.\n");
}