/* Target-dependent code for SuperH running NetBSD, for GDB.
   Copyright 2002 Free Software Foundation, Inc.
   Contributed by Wasabi Systems, 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 "defs.h"
#include "gdbcore.h"
#include "regcache.h"
#include "value.h"

#include "solib-svr4.h"

#include "nbsd-tdep.h"
#include "sh-tdep.h"
#include "shnbsd-tdep.h"

/* Convert an r0-r15 register number into an offset into a ptrace
   register structure.  */
static const int regmap[] =
{
  (20 * 4),	/* r0 */
  (19 * 4),	/* r1 */
  (18 * 4),	/* r2 */ 
  (17 * 4),	/* r3 */ 
  (16 * 4),	/* r4 */
  (15 * 4),	/* r5 */
  (14 * 4),	/* r6 */
  (13 * 4),	/* r7 */
  (12 * 4),	/* r8 */ 
  (11 * 4),	/* r9 */
  (10 * 4),	/* r10 */
  ( 9 * 4),	/* r11 */
  ( 8 * 4),	/* r12 */
  ( 7 * 4),	/* r13 */
  ( 6 * 4),	/* r14 */
  ( 5 * 4),	/* r15 */
};

#define SIZEOF_STRUCT_REG (21 * 4)

void
shnbsd_supply_reg (char *regs, int regno)
{
  int i;

  if (regno == PC_REGNUM || regno == -1)
    supply_register (PC_REGNUM, regs + (0 * 4));

  if (regno == SR_REGNUM || regno == -1)
    supply_register (SR_REGNUM, regs + (1 * 4));

  if (regno == PR_REGNUM || regno == -1)
    supply_register (PR_REGNUM, regs + (2 * 4));

  if (regno == MACH_REGNUM || regno == -1)
    supply_register (MACH_REGNUM, regs + (3 * 4));

  if (regno == MACL_REGNUM || regno == -1)
    supply_register (MACL_REGNUM, regs + (4 * 4));

  if ((regno >= R0_REGNUM && regno <= (R0_REGNUM + 15)) || regno == -1)
    {
      for (i = R0_REGNUM; i <= (R0_REGNUM + 15); i++)
	if (regno == i || regno == -1)
          supply_register (i, regs + regmap[i - R0_REGNUM]);
    }
}

void
shnbsd_fill_reg (char *regs, int regno)
{
  int i;

  if (regno == PC_REGNUM || regno == -1)
    regcache_collect (PC_REGNUM, regs + (0 * 4));

  if (regno == SR_REGNUM || regno == -1)
    regcache_collect (SR_REGNUM, regs + (1 * 4));

  if (regno == PR_REGNUM || regno == -1)
    regcache_collect (PR_REGNUM, regs + (2 * 4));

  if (regno == MACH_REGNUM || regno == -1)
    regcache_collect (MACH_REGNUM, regs + (3 * 4));

  if (regno == MACL_REGNUM || regno == -1)
    regcache_collect (MACL_REGNUM, regs + (4 * 4));

  if ((regno >= R0_REGNUM && regno <= (R0_REGNUM + 15)) || regno == -1)
    {
      for (i = R0_REGNUM; i <= (R0_REGNUM + 15); i++)
	if (regno == i || regno == -1)
          regcache_collect (i, regs + regmap[i - R0_REGNUM]);
    }
}

static void
fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
                      int which, CORE_ADDR ignore)
{
  /* We get everything from the .reg section.  */
  if (which != 0)
    return;

  if (core_reg_size < SIZEOF_STRUCT_REG)
    {
      warning ("Wrong size register set in core file.");
      return;
    }

  /* Integer registers.  */
  shnbsd_supply_reg (core_reg_sect, -1);
}

static void
fetch_elfcore_registers (char *core_reg_sect, unsigned core_reg_size,
                         int which, CORE_ADDR ignore)
{
  switch (which)
    {
    case 0:  /* Integer registers.  */
      if (core_reg_size != SIZEOF_STRUCT_REG)
	warning ("Wrong size register set in core file.");
      else
	shnbsd_supply_reg (core_reg_sect, -1);
      break;

    default:
      /* Don't know what kind of register request this is; just ignore it.  */
      break;
    }
}

static struct core_fns shnbsd_core_fns =
{
  bfd_target_unknown_flavour,		/* core_flavour */
  default_check_format,			/* check_format */
  default_core_sniffer,			/* core_sniffer */
  fetch_core_registers,			/* core_read_registers */
  NULL					/* next */
};

static struct core_fns shnbsd_elfcore_fns =
{
  bfd_target_elf_flavour,		/* core_flavour */
  default_check_format,			/* check_format */
  default_core_sniffer,			/* core_sniffer */
  fetch_elfcore_registers,		/* core_read_registers */
  NULL					/* next */
};

static int
shnbsd_pc_in_sigtramp (CORE_ADDR pc, char *func_name)
{
  /* FIXME: Need to add support for kernel-provided signal trampolines.  */
  return (nbsd_pc_in_sigtramp (pc, func_name));
}

static void
shnbsd_init_abi (struct gdbarch_info info,
                  struct gdbarch *gdbarch)
{
  set_gdbarch_pc_in_sigtramp (gdbarch, shnbsd_pc_in_sigtramp);

  set_solib_svr4_fetch_link_map_offsets (gdbarch,
		                nbsd_ilp32_solib_svr4_fetch_link_map_offsets);
}

void
_initialize_shnbsd_tdep (void)
{
  add_core_fns (&shnbsd_core_fns);
  add_core_fns (&shnbsd_elfcore_fns);

  gdbarch_register_osabi (bfd_arch_sh, GDB_OSABI_NETBSD_ELF, shnbsd_init_abi);
}