#include <signal.h>
#include "sysdep.h"
#include "bfd.h"

#include "v850_sim.h"

enum interrupt_type
{
  int_none,
  int_reset,
  int_nmi,
  int_intov1,
  int_intp10,
  int_intp11,
  int_intp12,
  int_intp13,
  int_intcm4,
  num_int_types
};

enum interrupt_cond_type
{
  int_cond_none,
  int_cond_pc,
  int_cond_time
};

struct interrupt_generator
{
  enum interrupt_type type;
  enum interrupt_cond_type cond_type;
  int number;
  int address;
  int time;
  int enabled;
  struct interrupt_generator *next;
};

char *interrupt_names[] = {
  "",
  "reset",
  "nmi",
  "intov1",
  "intp10",
  "intp11",
  "intp12",
  "intp13",
  "intcm4",
  NULL
};

struct interrupt_generator *intgen_list;

/* True if a non-maskable (such as NMI or reset) interrupt generator
   is present.  */

static int have_nm_generator;

#ifndef INLINE
#ifdef __GNUC__
#define INLINE inline
#else
#define INLINE
#endif
#endif

/* These default values correspond to expected usage for the chip.  */

SIM_ADDR rom_size = 0x8000;
SIM_ADDR low_end = 0x200000;
SIM_ADDR high_start = 0xffe000;

SIM_ADDR high_base;

host_callback *v850_callback;

int v850_debug;

static SIM_OPEN_KIND sim_kind;
static char *myname;

uint32 OP[4];

static struct hash_entry *lookup_hash PARAMS ((uint32 ins));
static long hash PARAMS ((long));
static void do_format_1_2 PARAMS ((uint32));
static void do_format_3 PARAMS ((uint32));
static void do_format_4 PARAMS ((uint32));
static void do_format_5 PARAMS ((uint32));
static void do_format_6 PARAMS ((uint32));
static void do_format_7 PARAMS ((uint32));
static void do_format_8 PARAMS ((uint32));
static void do_format_9_10 PARAMS ((uint32));
static void init_system PARAMS ((void));

#define MAX_HASH  63

struct hash_entry
{
  struct hash_entry *next;
  long opcode;
  long mask;
  struct simops *ops;
};

struct hash_entry hash_table[MAX_HASH+1];


static INLINE long 
hash(insn)
     long insn;
{
  if ((insn & 0x0600) == 0
      || (insn & 0x0700) == 0x0200
      || (insn & 0x0700) == 0x0600
      || (insn & 0x0780) == 0x0700)
    return (insn & 0x07e0) >> 5;

  if ((insn & 0x0700) == 0x0300
      || (insn & 0x0700) == 0x0400
      || (insn & 0x0700) == 0x0500)
    return (insn & 0x0780) >> 7;

  if ((insn & 0x07c0) == 0x0780)
    return (insn & 0x07c0) >> 6;

  return (insn & 0x07e0) >> 5;
}

static struct hash_entry *
lookup_hash (ins)
     uint32 ins;
{
  struct hash_entry *h;

  h = &hash_table[hash(ins)];

  while ((ins & h->mask) != h->opcode)
    {
      if (h->next == NULL)
	{
	  (*v850_callback->printf_filtered) (v850_callback, "ERROR looking up hash for 0x%x, PC=0x%x\n", ins, PC);
	  exit(1);
	}
      h = h->next;
    }
  return (h);
}

/* FIXME These would more efficient to use than load_mem/store_mem,
   but need to be changed to use the memory map.  */

uint8
get_byte (x)
     uint8 *x;
{
  return *x;
}

uint16
get_half (x)
     uint8 *x;
{
  uint8 *a = x;
  return (a[1] << 8) + (a[0]);
}

uint32
get_word (x)
      uint8 *x;
{
  uint8 *a = x;
  return (a[3]<<24) + (a[2]<<16) + (a[1]<<8) + (a[0]);
}

void
put_byte (addr, data)
     uint8 *addr;
     uint8 data;
{
  uint8 *a = addr;
  a[0] = data;
}

void
put_half (addr, data)
     uint8 *addr;
     uint16 data;
{
  uint8 *a = addr;
  a[0] = data & 0xff;
  a[1] = (data >> 8) & 0xff;
}

void
put_word (addr, data)
     uint8 *addr;
     uint32 data;
{
  uint8 *a = addr;
  a[0] = data & 0xff;
  a[1] = (data >> 8) & 0xff;
  a[2] = (data >> 16) & 0xff;
  a[3] = (data >> 24) & 0xff;
}

uint8 *
map (addr)
     SIM_ADDR addr;
{
  uint8 *p;

  /* Mask down to 24 bits. */
  addr &= 0xffffff;

  if (addr < low_end)
    {
      /* "Mirror" the addresses below 1MB. */
      if (addr < 0x100000)
	addr &= (rom_size - 1);
      else
	addr += (rom_size - 0x100000);
      return (uint8 *) (addr + State.mem);
    }
  else if (addr >= high_start)
    {
      /* If in the peripheral I/O region, mirror 1K region across 4K,
	 and similarly if in the internal RAM region.  */
      if (addr >= 0xfff000)
	addr &= 0xfff3ff;
      else if (addr >= 0xffe000)
	addr &= 0xffe3ff;
      return (uint8 *) (addr - high_start + high_base + State.mem);
    }
  else
    {
      /* Signal a memory error. */
      State.exception = SIGSEGV;
      /* Point to a location not in main memory - renders invalid
	 addresses harmless until we get back to main insn loop. */
      return (uint8 *) &(State.dummy_mem);
    }
}

uint32
load_mem (addr, len)
     SIM_ADDR addr;
     int len;
{
  uint8 *p = map (addr);

  switch (len)
    {
    case 1:
      return p[0];
    case 2:
      return p[1] << 8 | p[0];
    case 4:
      return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
    default:
      abort ();
    }
}

void
store_mem (addr, len, data)
     SIM_ADDR addr;
     int len;
     uint32 data;
{
  uint8 *p = map (addr);

  switch (len)
    {
    case 1:
      p[0] = data;
      return;
    case 2:
      p[0] = data;
      p[1] = data >> 8;
      return;
    case 4:
      p[0] = data;
      p[1] = data >> 8;
      p[2] = data >> 16;
      p[3] = data >> 24;
      return;
    default:
      abort ();
    }
}

static void
do_format_1_2 (insn)
     uint32 insn;
{
  struct hash_entry *h;

  h = lookup_hash (insn);
  OP[0] = insn & 0x1f;
  OP[1] = (insn >> 11) & 0x1f;
  (h->ops->func) ();
}

static void
do_format_3 (insn)
     uint32 insn;
{
  struct hash_entry *h;

  h = lookup_hash (insn);
  OP[0] = (((insn & 0x70) >> 4) | ((insn & 0xf800) >> 8)) << 1;
  (h->ops->func) ();
}

static void
do_format_4 (insn)
     uint32 insn;
{
  struct hash_entry *h;

  h = lookup_hash (insn);
  OP[0] = (insn >> 11) & 0x1f;
  OP[1] = (insn & 0x7f);
  (h->ops->func) ();
}

static void
do_format_5 (insn)
     uint32 insn;
{
  struct hash_entry *h;

  h = lookup_hash (insn);
  OP[0] = (((insn & 0x3f) << 15) | ((insn >> 17) & 0x7fff)) << 1;
  OP[1] = (insn >> 11) & 0x1f;
  (h->ops->func) ();
}

static void
do_format_6 (insn)
     uint32 insn;
{
  struct hash_entry *h;

  h = lookup_hash (insn);
  OP[0] = (insn >> 16) & 0xffff;
  OP[1] = insn & 0x1f;
  OP[2] = (insn >> 11) & 0x1f;
  (h->ops->func) ();
}

static void
do_format_7 (insn)
     uint32 insn;
{
  struct hash_entry *h;

  h = lookup_hash (insn);
  OP[0] = insn & 0x1f;
  OP[1] = (insn >> 11) & 0x1f;
  OP[2] = (insn >> 16) & 0xffff;
  (h->ops->func) ();
}

static void
do_format_8 (insn)
     uint32 insn;
{
  struct hash_entry *h;

  h = lookup_hash (insn);
  OP[0] = insn & 0x1f;
  OP[1] = (insn >> 11) & 0x7;
  OP[2] = (insn >> 16) & 0xffff;
  (h->ops->func) ();
}

static void
do_format_9_10 (insn)
     uint32 insn;
{
  struct hash_entry *h;

  h = lookup_hash (insn);
  OP[0] = insn & 0x1f;
  OP[1] = (insn >> 11) & 0x1f;
  (h->ops->func) ();
}

void
sim_size (power)
     int power;

{
  int totsize;

  if (State.mem)
    free (State.mem);

  totsize = rom_size + (low_end - 0x100000) + (0x1000000 - high_start);

  high_base = rom_size + (low_end - 0x100000);

  State.mem = (uint8 *) calloc (1, totsize);
  if (!State.mem)
    {
      (*v850_callback->printf_filtered) (v850_callback, "Allocation of main memory failed.\n");
      exit (1);
    }
}

void
sim_set_memory_map (spec)
     char *spec;
{
  char *reststr, *nreststr;
  SIM_ADDR new_low_end, new_high_start;

  new_low_end = low_end;
  new_high_start = high_start;
  if (! strncmp (spec, "hole=", 5))
    {
      new_low_end = sim_parse_number (spec + 5, &reststr);
      if (new_low_end < 0x100000)
	{
	  (*v850_callback->printf_filtered) (v850_callback,
					     "Low end must be at least 0x100000\n");
	  return;
	}
      if (*reststr == ',')
	{
	  ++reststr;
	  new_high_start = sim_parse_number (reststr, &nreststr);
	  /* FIXME Check high_start also */
	}
      (*v850_callback->printf_filtered) (v850_callback,
					 "Hole goes from 0x%x to 0x%x\n",
					 new_low_end, new_high_start);
    }
  else
    {
      (*v850_callback->printf_filtered) (v850_callback, "Invalid specification for memory map, must be `hole=<m>[,<n>]'\n");
    }

  if (new_low_end != low_end || new_high_start != high_start)
    {
      low_end = new_low_end;
      high_start = new_high_start;
      if (State.mem)
	{
	  (*v850_callback->printf_filtered) (v850_callback, "Reconfiguring memory (old contents will be lost)\n");
	  sim_size (1);
	}
    }
}

/* Parse a number in hex, octal, or decimal form.  */

int
sim_parse_number (str, rest)
     char *str, **rest;
{
  if (str[0] == '0' && str[1] == 'x')
    return strtol (str, rest, 16);
  else if (str[0] == '0')
    return strtol (str, rest, 16);
  else
    return strtol (str, rest, 10);
}

static void
init_system ()
{
  if (!State.mem)
    sim_size(1);
}

int
sim_write (sd, addr, buffer, size)
     SIM_DESC sd;
     SIM_ADDR addr;
     unsigned char *buffer;
     int size;
{
  int i;

  init_system ();

  for (i = 0; i < size; i++)
    store_mem (addr + i, 1, buffer[i]);

  return size;
}

SIM_DESC
sim_open (kind,argv)
     SIM_OPEN_KIND kind;
     char **argv;
{
  struct simops *s;
  struct hash_entry *h;
  char **p;

  sim_kind = kind;
  myname = argv[0];

  for (p = argv + 1; *p; ++p)
    {
      if (strcmp (*p, "-E") == 0)
	++p; /* ignore endian spec */
      else
#ifdef DEBUG
      if (strcmp (*p, "-t") == 0)
	v850_debug = DEBUG;
      else
#endif
	(*v850_callback->printf_filtered) (v850_callback, "ERROR: unsupported option(s): %s\n",*p);
    }

  /* put all the opcodes in the hash table */
  for (s = Simops; s->func; s++)
    {
      h = &hash_table[hash(s->opcode)];
      
      /* go to the last entry in the chain */
      while (h->next)
	  h = h->next;

      if (h->ops)
	{
	  h->next = (struct hash_entry *) calloc(1,sizeof(struct hash_entry));
	  h = h->next;
	}
      h->ops = s;
      h->mask = s->mask;
      h->opcode = s->opcode;
    }

  /* fudge our descriptor for now */
  return (SIM_DESC) 1;
}


void
sim_close (sd, quitting)
     SIM_DESC sd;
     int quitting;
{
  /* nothing to do */
}

void
sim_set_profile (n)
     int n;
{
  (*v850_callback->printf_filtered) (v850_callback, "sim_set_profile %d\n", n);
}

void
sim_set_profile_size (n)
     int n;
{
  (*v850_callback->printf_filtered) (v850_callback, "sim_set_profile_size %d\n", n);
}

time_t start_time;

static void do_interrupt PARAMS ((enum interrupt_type));

int
sim_stop (sd)
     SIM_DESC sd;
{
  return 0;
}

void
sim_resume (sd, step, siggnal)
     SIM_DESC sd;
     int step, siggnal;
{
  uint32 inst, opcode;
  reg_t oldpc;
  struct interrupt_generator *intgen;
  time_t now;

  if (step)
    State.exception = SIGTRAP;
  else
    State.exception = 0;

  time (&start_time);

  do
    {
      /* Fetch the current instruction.  */
      inst = RLW (PC);
      oldpc = PC;
      opcode = (inst & 0x07e0) >> 5;

      /* Decode the opcode field. */
      if ((opcode & 0x30) == 0
	  || (opcode & 0x38) == 0x10)
	{
	  do_format_1_2 (inst & 0xffff);
	  PC += 2;
	}
      else if ((opcode & 0x3C) == 0x18
	       || (opcode & 0x3C) == 0x1C
	       || (opcode & 0x3C) == 0x20
	       || (opcode & 0x3C) == 0x24
	       || (opcode & 0x3C) == 0x28)
	{
	  do_format_4 (inst & 0xffff);
	  PC += 2;
	}
      else if ((opcode & 0x3C) == 0x2C)
	{
	  do_format_3 (inst & 0xffff);
	  /* No PC update, it's done in the instruction.  */
	}
      else if ((opcode & 0x38) == 0x30)
	{
	  do_format_6 (inst);
	  PC += 4;
	}
      else if ((opcode & 0x3C) == 0x38)
	{
	  do_format_7 (inst);
	  PC += 4;
	}
      else if ((opcode & 0x3E) == 0x3C)
	{
	  do_format_5 (inst);
	  /* No PC update, it's done in the instruction.  */
	}
      else if ((opcode & 0x3F) == 0x3E)
	{
	  do_format_8 (inst);
	  PC += 4;
	}
      else
	{
	  do_format_9_10 (inst);
	  PC += 4;
	}

      /* Check for and handle pending interrupts.  */
      if (intgen_list && (have_nm_generator || !(PSW & PSW_ID)))
	{
	  intgen = NULL;
	  for (intgen = intgen_list; intgen != NULL; intgen = intgen->next)
	    {
	      if (intgen->cond_type == int_cond_pc
		  && oldpc == intgen->address
		  && intgen->enabled)
		{
		  break;
		}
	      else if (intgen->cond_type == int_cond_time
		       && intgen->enabled)
		{
		  time (&now);
		  if (((long) now - (long) start_time) > intgen->time)
		    {
		      intgen->enabled = 0;
		      break;
		    }
		}
	    }
	  if (intgen)
	    do_interrupt (intgen->type);
	}
      else if (State.pending_nmi)
	{
	  State.pending_nmi = 0;
	  do_interrupt (int_nmi);
	}
    }
  while (!State.exception);
}

static void
do_interrupt (inttype)
     enum interrupt_type inttype;
{
  /* Disable further interrupts.  */
  PSW |= PSW_ID;
  /* Indicate that we're doing interrupt not exception processing.  */
  PSW &= ~PSW_EP;
  if (inttype == int_reset)
    {
      PC = 0;
      PSW = 0x20;
      ECR = 0;
      /* (Might be useful to init other regs with random values.) */
    }
  else if (inttype == int_nmi)
    {
      if (PSW & PSW_NP)
	{
	  /* We're already working on an NMI, so this one must wait
	     around until the previous one is done.  The processor
	     ignores subsequent NMIs, so we don't need to count them.  */
	  State.pending_nmi = 1;
	}
      else
	{
	  FEPC = PC;
	  FEPSW = PSW;
	  /* Set the FECC part of the ECR. */
	  ECR &= 0x0000ffff;
	  ECR |= 0x10;
	  PSW |= PSW_NP;
	  PC = 0x10;
	}
    }
  else
    {
      EIPC = PC;
      EIPSW = PSW;
      /* Clear the EICC part of the ECR, will set below. */
      ECR &= 0xffff0000;
      switch (inttype)
	{
	case int_intov1:
	  PC = 0x80;
	  ECR |= 0x80;
	  break;
	case int_intp10:
	  PC = 0x90;
	  ECR |= 0x90;
	  break;
	case int_intp11:
	  PC = 0xa0;
	  ECR |= 0xa0;
	  break;
	case int_intp12:
	  PC = 0xb0;
	  ECR |= 0xb0;
	  break;
	case int_intp13:
	  PC = 0xc0;
	  ECR |= 0xc0;
	  break;
	case int_intcm4:
	  PC = 0xd0;
	  ECR |= 0xd0;
	  break;
	default:
	  /* Should never be possible.  */
	  abort ();
	  break;
	}
    }
}

int
sim_trace (sd)
     SIM_DESC sd;
{
#ifdef DEBUG
  v850_debug = DEBUG;
#endif
  sim_resume (sd, 0, 0);
  return 1;
}

void
sim_info (sd, verbose)
     SIM_DESC sd;
     int verbose;
{
  (*v850_callback->printf_filtered) (v850_callback, "sim_info\n");
}

SIM_RC
sim_create_inferior (sd, argv, env)
     SIM_DESC sd;
     char **argv;
     char **env;
{
  return SIM_RC_OK;
}

void
sim_kill (sd)
     SIM_DESC sd;
{
  /* nothing to do */
}

void
sim_set_callbacks (sd, p)
     SIM_DESC sd;
     host_callback *p;
{
  v850_callback = p;
}

/* All the code for exiting, signals, etc needs to be revamped.

   This is enough to get c-torture limping though.  */

void
sim_stop_reason (sd, reason, sigrc)
     SIM_DESC sd;
     enum sim_stop *reason;
     int *sigrc;
{
  if (State.exception == SIG_V850_EXIT)
    {
      *reason = sim_exited;
      *sigrc = State.regs[7];
    }
  else
    {
      *reason = sim_stopped;
      *sigrc = State.exception;
    }
}

void
sim_fetch_register (sd, rn, memory)
     SIM_DESC sd;
     int rn;
     unsigned char *memory;
{
  put_word (memory, State.regs[rn]);
}
 
void
sim_store_register (sd, rn, memory)
     SIM_DESC sd;
     int rn;
     unsigned char *memory;
{
  State.regs[rn] = get_word (memory);
}

int
sim_read (sd, addr, buffer, size)
     SIM_DESC sd;
     SIM_ADDR addr;
     unsigned char *buffer;
     int size;
{
  int i;
  for (i = 0; i < size; i++)
    buffer[i] = load_mem (addr + i, 1);

  return size;
} 

int current_intgen_number = 1;

void
sim_set_interrupt (spec)
     char *spec;
{
  int i, num;
  char **argv;
  struct interrupt_generator *intgen, *tmpgen;
  extern char **buildargv ();

  argv = buildargv (spec);

  if (*argv && ! strcmp (*argv, "add"))
    {
      /* Create a new interrupt generator object.  */
      intgen = (struct interrupt_generator *)
	malloc (sizeof(struct interrupt_generator));
      intgen->type = int_none;
      intgen->cond_type = int_cond_none;
      intgen->address = 0;
      intgen->time = 0;
      intgen->enabled = 0;
      ++argv;
      /* Match on interrupt type name.  */
      for (i = 0; i < num_int_types; ++i)
	{
	  if (*argv && ! strcmp (*argv, interrupt_names[i]))
	    {
	      intgen->type = i;
	      break;
	    }
	}
      if (intgen->type == int_none)
	{
	  (*v850_callback->printf_filtered) (v850_callback, "Interrupt type unknown; known types are\n");
	  for (i = 0; i < num_int_types; ++i)
	    {
	      (*v850_callback->printf_filtered) (v850_callback, " %s", interrupt_names[i]);
	    }
	  (*v850_callback->printf_filtered) (v850_callback, "\n");
	  free (intgen);
	  return;
	}
      ++argv;
      intgen->address = 0;
      intgen->time = 0;
      if (*argv && ! strcmp (*argv, "pc"))
	{
	  intgen->cond_type = int_cond_pc;
	  ++argv;
	  intgen->address = sim_parse_number (*argv, NULL);
	}
      else if (*argv && ! strcmp (*argv, "time"))
	{
	  intgen->cond_type = int_cond_time;
	  ++argv;
	  intgen->time = sim_parse_number (*argv, NULL);
	}
      else
	{
	  (*v850_callback->printf_filtered) (v850_callback, "Condition type must be `pc' or `time'.\n");
	  free (intgen);
	  return;
	}
      /* We now have a valid interrupt generator.  Number it and add
	 to the list of generators.  */
      intgen->number = current_intgen_number++;
      intgen->enabled = 1;
      intgen->next = intgen_list;
      intgen_list = intgen;
      (*v850_callback->printf_filtered) (v850_callback, "Interrupt generator %d (NMI) at pc=0x%x, time=%d.\n", intgen_list->number, intgen_list->address, intgen_list->time);
    }
  else if (*argv && !strcmp (*argv, "remove"))
    {
      ++argv;
      num = sim_parse_number (*argv, NULL);
      tmpgen = NULL;
      if (intgen_list)
	{
	  if (intgen_list->number == num)
	    {
	      tmpgen = intgen_list;
	      intgen_list = intgen_list->next;
	    }
	  else
	    {
	      for (intgen = intgen_list; intgen != NULL; intgen = intgen->next)
		{
		  if (intgen->next != NULL && intgen->next->number == num)
		    {
		      tmpgen = intgen->next;
		      intgen->next = intgen->next->next;
		      break;
		    }
		}
	    }
	  if (tmpgen)
	    free (tmpgen);
	  else
	    (*v850_callback->printf_filtered) (v850_callback,
					       "No interrupt generator numbered %d, ignoring.\n", num);
	}
    }
  else if (*argv && !strcmp (*argv, "info"))
    {
      if (intgen_list)
	{
	  for (intgen = intgen_list; intgen != NULL; intgen = intgen->next)
	    (*v850_callback->printf_filtered) (v850_callback,
					       "Interrupt generator %d (%s) at pc=0x%x/time=%d%s.\n",
					       intgen->number,
					       interrupt_names[intgen->type],
					       intgen->address,
					       intgen->time,
					       (intgen->enabled ? "" : " (disabled)"));
	}
      else
	{
	  (*v850_callback->printf_filtered) (v850_callback, "No interrupt generators defined.\n"); 
	}

    }
  else
    {
      (*v850_callback->printf_filtered) (v850_callback,
					 "Invalid interrupt command, must be one of `add', `remove', or `info'.\n");
    }
  /* Cache the presence of a non-maskable generator.  */
  have_nm_generator = 0;
  for (intgen = intgen_list; intgen != NULL; intgen = intgen->next)
    {
      if (intgen->type == int_nmi || intgen->type == int_reset)
	{
	  have_nm_generator = 1;
	  break;
	}
    }
}

void
sim_do_command (sd, cmd)
     SIM_DESC sd;
     char *cmd;
{
  char *mm_cmd = "memory-map";
  char *int_cmd = "interrupt";

  if (! strncmp (cmd, mm_cmd, strlen (mm_cmd))
      && strchr (" 	", cmd[strlen(mm_cmd)]))
    sim_set_memory_map (cmd + strlen(mm_cmd) + 1);

  else if (! strncmp (cmd, int_cmd, strlen (int_cmd))
      && strchr (" 	", cmd[strlen(int_cmd)]))
    sim_set_interrupt (cmd + strlen(int_cmd) + 1);

  else if (! strcmp (cmd, "help"))
    {
      (*v850_callback->printf_filtered) (v850_callback, "V850 simulator commands:\n\n");
      (*v850_callback->printf_filtered) (v850_callback, "interrupt add <inttype> { pc | time } <value> -- Set up an interrupt generator\n");
      (*v850_callback->printf_filtered) (v850_callback, "interrupt remove <n> -- Remove an existing interrupt generator\n");
      (*v850_callback->printf_filtered) (v850_callback, "interrupt info -- List all the interrupt generators\n");
      (*v850_callback->printf_filtered) (v850_callback, "memory-map hole=<m>,<n> -- Set the memory map to have a hole between <m> and <n>\n");
      (*v850_callback->printf_filtered) (v850_callback, "\n");
    }
  else
    (*v850_callback->printf_filtered) (v850_callback, "\"%s\" is not a valid V850 simulator command.\n",
				       cmd);
}

SIM_RC
sim_load (sd, prog, abfd, from_tty)
     SIM_DESC sd;
     char *prog;
     bfd *abfd;
     int from_tty;
{
  extern bfd *sim_load_file (); /* ??? Don't know where this should live.  */
  bfd *prog_bfd;

  prog_bfd = sim_load_file (sd, myname, v850_callback, prog, abfd,
			    sim_kind == SIM_OPEN_DEBUG);
  if (prog_bfd == NULL)
    return SIM_RC_FAIL;
  PC = bfd_get_start_address (prog_bfd);
  if (abfd == NULL)
    bfd_close (prog_bfd);
  return SIM_RC_OK;
} 
