/* Low level interface to simulators, for the remote server for GDB.
   Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
   Free Software Foundation, Inc.

   This file is part of GDB.

   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 2 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, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include "server.h"
#include "bfd.h"
#include "callback.h"		/* GDB simulator callback interface */
#include "remote-sim.h"		/* GDB simulator interface */

extern int remote_debug;

extern host_callback default_callback;	/* in sim/common/callback.c */

static char my_registers[REGISTER_BYTES] __attribute__ ((aligned));
char * registers = my_registers;

int target_byte_order;		/* used by simulator */

/* We record the result of sim_open so we can pass it
   back to the other sim_foo routines.  */
static SIM_DESC gdbsim_desc = 0;

/* This version of "load" should be usable for any simulator that
   does not support loading itself.  */

static void
mygeneric_load (bfd *loadfile_bfd)
{
  asection *s;

  for (s = loadfile_bfd->sections; s; s = s->next)
    {
      if (s->flags & SEC_LOAD)
	{
	  bfd_size_type size;

	  size = bfd_get_section_size_before_reloc (s);
	  if (size > 0)
	    {
	      char *buffer;
	      bfd_vma lma;	/* use load address, not virtual address */

	      buffer = xmalloc (size);
	      lma = s->lma;

	      /* Is this really necessary?  I guess it gives the user something
	         to look at during a long download.  */
	      printf ("Loading section %s, size 0x%lx lma 0x%lx\n",
		      bfd_get_section_name (loadfile_bfd, s),
		      (unsigned long) size,
		      (unsigned long) lma);	/* chops high 32 bits.  FIXME!! */

	      bfd_get_section_contents (loadfile_bfd, s, buffer, 0, size);

	      write_inferior_memory (lma, buffer, size);
	      free (buffer);
	    }
	}
    }

  printf ("Start address 0x%lx\n",
	  (unsigned long) loadfile_bfd->start_address);

  /* We were doing this in remote-mips.c, I suspect it is right
     for other targets too.  */
  /* write_pc (loadfile_bfd->start_address); *//* FIXME!! */
}

int
create_inferior (char *program, char **argv)
{
  bfd *abfd;
  int pid = 0;
  char **new_argv;
  int nargs;

  abfd = bfd_openr (program, 0);
  if (!abfd)
    {
      fprintf (stderr, "gdbserver: can't open %s: %s\n",
	       program, bfd_errmsg (bfd_get_error ()));
      exit (1);
    }

  if (!bfd_check_format (abfd, bfd_object))
    {
      fprintf (stderr, "gdbserver: unknown load format for %s: %s\n",
	       program, bfd_errmsg (bfd_get_error ()));
      exit (1);
    }

  /* Add "-E big" or "-E little" to the argument list depending on the
     endianness of the program to be loaded.  */
  for (nargs = 0; argv[nargs] != NULL; nargs++)		/* count the args */
    ;
  new_argv = alloca (sizeof (char *) * (nargs + 3));	/* allocate new args */
  for (nargs = 0; argv[nargs] != NULL; nargs++)		/* copy old to new */
    new_argv[nargs] = argv[nargs];
  new_argv[nargs] = "-E";
  new_argv[nargs + 1] = bfd_big_endian (abfd) ? "big" : "little";
  new_argv[nargs + 2] = NULL;
  argv = new_argv;

  /* Create an instance of the simulator.  */
  default_callback.init (&default_callback);
  gdbsim_desc = sim_open (SIM_OPEN_STANDALONE, &default_callback, abfd, argv);
  if (gdbsim_desc == 0)
    exit (1);

  /* Load the program into the simulator.  */
  if (abfd)
    if (sim_load (gdbsim_desc, program, NULL, 0) == SIM_RC_FAIL)
      mygeneric_load (abfd);

  /* Create an inferior process in the simulator.  This initializes SP.  */
  sim_create_inferior (gdbsim_desc, abfd, argv, /* env */ NULL);
  sim_resume (gdbsim_desc, 1, 0);	/* execute one instr */
  return pid;
}

/* Attaching is not supported.  */
int
myattach (int pid)
{
  return -1;
}

/* Kill the inferior process.  Make us have no inferior.  */

void
kill_inferior (void)
{
  sim_close (gdbsim_desc, 0);
  default_callback.shutdown (&default_callback);
}

/* Fetch one register.  */

static void
fetch_register (int regno)
{
  sim_fetch_register (gdbsim_desc, regno, &registers[REGISTER_BYTE (regno)],
		      REGISTER_RAW_SIZE (regno));
}

/* Fetch all registers, or just one, from the child process.  */

void
fetch_inferior_registers (int regno)
{
  if (regno == -1 || regno == 0)
    for (regno = 0; regno < NUM_REGS /*-NUM_FREGS*/ ; regno++)
      fetch_register (regno);
  else
    fetch_register (regno);
}

/* Store our register values back into the inferior.
   If REGNO is -1, do this for all registers.
   Otherwise, REGNO specifies which register (so we can save time).  */

void
store_inferior_registers (int regno)
{
  if (regno == -1)
    {
      for (regno = 0; regno < NUM_REGS; regno++)
	store_inferior_registers (regno);
    }
  else
    sim_store_register (gdbsim_desc, regno, &registers[REGISTER_BYTE (regno)],
			REGISTER_RAW_SIZE (regno));
}

/* Return nonzero if the given thread is still alive.  */
int
mythread_alive (int pid)
{
  return 1;
}

/* Wait for process, returns status */

unsigned char
mywait (char *status)
{
  int sigrc;
  enum sim_stop reason;

  sim_stop_reason (gdbsim_desc, &reason, &sigrc);
  switch (reason)
    {
    case sim_exited:
      if (remote_debug)
	printf ("\nChild exited with retcode = %x \n", sigrc);
      *status = 'W';
      return sigrc;

#if 0
    case sim_stopped:
      if (remote_debug)
	printf ("\nChild terminated with signal = %x \n", sigrc);
      *status = 'X';
      return sigrc;
#endif

    default:			/* should this be sim_signalled or sim_stopped?  FIXME!! */
      if (remote_debug)
	printf ("\nChild received signal = %x \n", sigrc);
      fetch_inferior_registers (0);
      *status = 'T';
      return (unsigned char) sigrc;
    }
}

/* Resume execution of the inferior process.
   If STEP is nonzero, single-step it.
   If SIGNAL is nonzero, give it that signal.  */

void
myresume (int step, int signo)
{
  /* Should be using target_signal_to_host() or signal numbers in target.h
     to convert GDB signal number to target signal number.  */
  sim_resume (gdbsim_desc, step, signo);
}

/* Copy LEN bytes from inferior's memory starting at MEMADDR
   to debugger memory starting at MYADDR.  */

void
read_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len)
{
  sim_read (gdbsim_desc, memaddr, myaddr, len);
}

/* Copy LEN bytes of data from debugger memory at MYADDR
   to inferior's memory at MEMADDR.
   On failure (cannot write the inferior)
   returns the value of errno.  */

int
write_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len)
{
  sim_write (gdbsim_desc, memaddr, myaddr, len);	/* should check for error.  FIXME!! */
  return 0;
}

void
initialize_low (void)
{
}