/* Simulator for the moxie processor
   Copyright (C) 2008-2016 Free Software Foundation, Inc.
   Contributed by Anthony Green

This file is part of GDB, the GNU debugger.

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 <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/times.h>
#include <sys/param.h>
#include <unistd.h>
#include "bfd.h"
#include "libiberty.h"
#include "gdb/remote-sim.h"

#include "sim-main.h"
#include "sim-base.h"
#include "sim-options.h"

typedef int word;
typedef unsigned int uword;

/* Extract the signed 10-bit offset from a 16-bit branch
   instruction.  */
#define INST2OFFSET(o) ((((signed short)((o & ((1<<10)-1))<<6))>>6)<<1)

#define EXTRACT_WORD(addr) \
  ((sim_core_read_aligned_1 (scpu, cia, read_map, addr) << 24) \
   + (sim_core_read_aligned_1 (scpu, cia, read_map, addr+1) << 16) \
   + (sim_core_read_aligned_1 (scpu, cia, read_map, addr+2) << 8) \
   + (sim_core_read_aligned_1 (scpu, cia, read_map, addr+3)))

#define EXTRACT_OFFSET(addr)						\
  (unsigned int)							\
  (((signed short)							\
    ((sim_core_read_aligned_1 (scpu, cia, read_map, addr) << 8)		\
     + (sim_core_read_aligned_1 (scpu, cia, read_map, addr+1))) << 16) >> 16)

static unsigned long
moxie_extract_unsigned_integer (unsigned char *addr, int len)
{
  unsigned long retval;
  unsigned char * p;
  unsigned char * startaddr = (unsigned char *)addr;
  unsigned char * endaddr = startaddr + len;
 
  if (len > (int) sizeof (unsigned long))
    printf ("That operation is not available on integers of more than %zu bytes.",
	    sizeof (unsigned long));
 
  /* Start at the most significant end of the integer, and work towards
     the least significant.  */
  retval = 0;

  for (p = endaddr; p > startaddr;)
    retval = (retval << 8) | * -- p;
  
  return retval;
}

static void
moxie_store_unsigned_integer (unsigned char *addr, int len, unsigned long val)
{
  unsigned char * p;
  unsigned char * startaddr = (unsigned char *)addr;
  unsigned char * endaddr = startaddr + len;

  for (p = endaddr; p > startaddr;)
    {
      * -- p = val & 0xff;
      val >>= 8;
    }
}

/* moxie register names.  */
static const char *reg_names[16] = 
  { "$fp", "$sp", "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", 
    "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13" };

/* The machine state.

   This state is maintained in host byte order.  The fetch/store
   register functions must translate between host byte order and the
   target processor byte order.  Keeping this data in target byte
   order simplifies the register read/write functions.  Keeping this
   data in native order improves the performance of the simulator.
   Simulation speed is deemed more important.  */

#define NUM_MOXIE_REGS 17 /* Including PC */
#define NUM_MOXIE_SREGS 256 /* The special registers */
#define PC_REGNO     16

/* The ordering of the moxie_regset structure is matched in the
   gdb/config/moxie/tm-moxie.h file in the REGISTER_NAMES macro.  */
/* TODO: This should be moved to sim-main.h:_sim_cpu.  */
struct moxie_regset
{
  word		  regs[NUM_MOXIE_REGS + 1]; /* primary registers */
  word		  sregs[256];             /* special registers */
  word            cc;                   /* the condition code reg */
  unsigned long long insts;                /* instruction counter */
};

#define CC_GT  1<<0
#define CC_LT  1<<1
#define CC_EQ  1<<2
#define CC_GTU 1<<3
#define CC_LTU 1<<4

/* TODO: This should be moved to sim-main.h:_sim_cpu.  */
union
{
  struct moxie_regset asregs;
  word asints [1];		/* but accessed larger... */
} cpu;

static void
set_initial_gprs (void)
{
  int i;
  long space;
  
  /* Set up machine just out of reset.  */
  cpu.asregs.regs[PC_REGNO] = 0;
  
  /* Clean out the register contents.  */
  for (i = 0; i < NUM_MOXIE_REGS; i++)
    cpu.asregs.regs[i] = 0;
  for (i = 0; i < NUM_MOXIE_SREGS; i++)
    cpu.asregs.sregs[i] = 0;
}

/* Write a 1 byte value to memory.  */

static INLINE void
wbat (sim_cpu *scpu, word pc, word x, word v)
{
  address_word cia = CPU_PC_GET (scpu);
  
  sim_core_write_aligned_1 (scpu, cia, write_map, x, v);
}

/* Write a 2 byte value to memory.  */

static INLINE void
wsat (sim_cpu *scpu, word pc, word x, word v)
{
  address_word cia = CPU_PC_GET (scpu);
  
  sim_core_write_aligned_2 (scpu, cia, write_map, x, v);
}

/* Write a 4 byte value to memory.  */

static INLINE void
wlat (sim_cpu *scpu, word pc, word x, word v)
{
  address_word cia = CPU_PC_GET (scpu);
	
  sim_core_write_aligned_4 (scpu, cia, write_map, x, v);
}

/* Read 2 bytes from memory.  */

static INLINE int
rsat (sim_cpu *scpu, word pc, word x)
{
  address_word cia = CPU_PC_GET (scpu);
  
  return (sim_core_read_aligned_2 (scpu, cia, read_map, x));
}

/* Read 1 byte from memory.  */

static INLINE int
rbat (sim_cpu *scpu, word pc, word x)
{
  address_word cia = CPU_PC_GET (scpu);
  
  return (sim_core_read_aligned_1 (scpu, cia, read_map, x));
}

/* Read 4 bytes from memory.  */

static INLINE int
rlat (sim_cpu *scpu, word pc, word x)
{
  address_word cia = CPU_PC_GET (scpu);
  
  return (sim_core_read_aligned_4 (scpu, cia, read_map, x));
}

#define CHECK_FLAG(T,H) if (tflags & T) { hflags |= H; tflags ^= T; }

static unsigned int
convert_target_flags (unsigned int tflags)
{
  unsigned int hflags = 0x0;

  CHECK_FLAG(0x0001, O_WRONLY);
  CHECK_FLAG(0x0002, O_RDWR);
  CHECK_FLAG(0x0008, O_APPEND);
  CHECK_FLAG(0x0200, O_CREAT);
  CHECK_FLAG(0x0400, O_TRUNC);
  CHECK_FLAG(0x0800, O_EXCL);
  CHECK_FLAG(0x2000, O_SYNC);

  if (tflags != 0x0)
    fprintf (stderr, 
	     "Simulator Error: problem converting target open flags for host.  0x%x\n", 
	     tflags);

  return hflags;
}

/* TODO: Split this up into finger trace levels than just insn.  */
#define MOXIE_TRACE_INSN(str) \
  TRACE_INSN (scpu, "0x%08x, %s, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x", \
	      opc, str, cpu.asregs.regs[0], cpu.asregs.regs[1], \
	      cpu.asregs.regs[2], cpu.asregs.regs[3], cpu.asregs.regs[4], \
	      cpu.asregs.regs[5], cpu.asregs.regs[6], cpu.asregs.regs[7], \
	      cpu.asregs.regs[8], cpu.asregs.regs[9], cpu.asregs.regs[10], \
	      cpu.asregs.regs[11], cpu.asregs.regs[12], cpu.asregs.regs[13], \
	      cpu.asregs.regs[14], cpu.asregs.regs[15])

void
sim_engine_run (SIM_DESC sd,
		int next_cpu_nr, /* ignore  */
		int nr_cpus, /* ignore  */
		int siggnal) /* ignore  */
{
  word pc, opc;
  unsigned short inst;
  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
  address_word cia = CPU_PC_GET (scpu);

  pc = cpu.asregs.regs[PC_REGNO];

  /* Run instructions here. */
  do 
    {
      opc = pc;

      /* Fetch the instruction at pc.  */
      inst = (sim_core_read_aligned_1 (scpu, cia, read_map, pc) << 8)
	+ sim_core_read_aligned_1 (scpu, cia, read_map, pc+1);

      /* Decode instruction.  */
      if (inst & (1 << 15))
	{
	  if (inst & (1 << 14))
	    {
	      /* This is a Form 3 instruction.  */
	      int opcode = (inst >> 10 & 0xf);

	      switch (opcode)
		{
		case 0x00: /* beq */
		  {
		    MOXIE_TRACE_INSN ("beq");
		    if (cpu.asregs.cc & CC_EQ)
		      pc += INST2OFFSET(inst);
		  }
		  break;
		case 0x01: /* bne */
		  {
		    MOXIE_TRACE_INSN ("bne");
		    if (! (cpu.asregs.cc & CC_EQ))
		      pc += INST2OFFSET(inst);
		  }
		  break;
		case 0x02: /* blt */
		  {
		    MOXIE_TRACE_INSN ("blt");
		    if (cpu.asregs.cc & CC_LT)
		      pc += INST2OFFSET(inst);
		  }		  break;
		case 0x03: /* bgt */
		  {
		    MOXIE_TRACE_INSN ("bgt");
		    if (cpu.asregs.cc & CC_GT)
		      pc += INST2OFFSET(inst);
		  }
		  break;
		case 0x04: /* bltu */
		  {
		    MOXIE_TRACE_INSN ("bltu");
		    if (cpu.asregs.cc & CC_LTU)
		      pc += INST2OFFSET(inst);
		  }
		  break;
		case 0x05: /* bgtu */
		  {
		    MOXIE_TRACE_INSN ("bgtu");
		    if (cpu.asregs.cc & CC_GTU)
		      pc += INST2OFFSET(inst);
		  }
		  break;
		case 0x06: /* bge */
		  {
		    MOXIE_TRACE_INSN ("bge");
		    if (cpu.asregs.cc & (CC_GT | CC_EQ))
		      pc += INST2OFFSET(inst);
		  }
		  break;
		case 0x07: /* ble */
		  {
		    MOXIE_TRACE_INSN ("ble");
		    if (cpu.asregs.cc & (CC_LT | CC_EQ))
		      pc += INST2OFFSET(inst);
		  }
		  break;
		case 0x08: /* bgeu */
		  {
		    MOXIE_TRACE_INSN ("bgeu");
		    if (cpu.asregs.cc & (CC_GTU | CC_EQ))
		      pc += INST2OFFSET(inst);
		  }
		  break;
		case 0x09: /* bleu */
		  {
		    MOXIE_TRACE_INSN ("bleu");
		    if (cpu.asregs.cc & (CC_LTU | CC_EQ))
		      pc += INST2OFFSET(inst);
		  }
		  break;
		default:
		  {
		    MOXIE_TRACE_INSN ("SIGILL3");
		    sim_engine_halt (sd, NULL, NULL, pc, sim_stopped, SIM_SIGILL);
		    break;
		  }
		}
	    }
	  else
	    {
	      /* This is a Form 2 instruction.  */
	      int opcode = (inst >> 12 & 0x3);
	      switch (opcode)
		{
		case 0x00: /* inc */
		  {
		    int a = (inst >> 8) & 0xf;
		    unsigned av = cpu.asregs.regs[a];
		    unsigned v = (inst & 0xff);

		    MOXIE_TRACE_INSN ("inc");
		    cpu.asregs.regs[a] = av + v;
		  }
		  break;
		case 0x01: /* dec */
		  {
		    int a = (inst >> 8) & 0xf;
		    unsigned av = cpu.asregs.regs[a];
		    unsigned v = (inst & 0xff);

		    MOXIE_TRACE_INSN ("dec");
		    cpu.asregs.regs[a] = av - v;
		  }
		  break;
		case 0x02: /* gsr */
		  {
		    int a = (inst >> 8) & 0xf;
		    unsigned v = (inst & 0xff);

		    MOXIE_TRACE_INSN ("gsr");
		    cpu.asregs.regs[a] = cpu.asregs.sregs[v];
		  }
		  break;
		case 0x03: /* ssr */
		  {
		    int a = (inst >> 8) & 0xf;
		    unsigned v = (inst & 0xff);

		    MOXIE_TRACE_INSN ("ssr");
		    cpu.asregs.sregs[v] = cpu.asregs.regs[a];
		  }
		  break;
		default:
		  MOXIE_TRACE_INSN ("SIGILL2");
		  sim_engine_halt (sd, NULL, NULL, pc, sim_stopped, SIM_SIGILL);
		  break;
		}
	    }
	}
      else
	{
	  /* This is a Form 1 instruction.  */
	  int opcode = inst >> 8;
	  switch (opcode)
	    {
	    case 0x00: /* bad */
	      opc = opcode;
	      MOXIE_TRACE_INSN ("SIGILL0");
	      sim_engine_halt (sd, NULL, NULL, pc, sim_stopped, SIM_SIGILL);
	      break;
	    case 0x01: /* ldi.l (immediate) */
	      {
		int reg = (inst >> 4) & 0xf;
		unsigned int val = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("ldi.l");
		cpu.asregs.regs[reg] = val;
		pc += 4;
	      }
	      break;
	    case 0x02: /* mov (register-to-register) */
	      {
		int dest  = (inst >> 4) & 0xf;
		int src = (inst ) & 0xf;

		MOXIE_TRACE_INSN ("mov");
		cpu.asregs.regs[dest] = cpu.asregs.regs[src];
	      }
	      break;
 	    case 0x03: /* jsra */
 	      {
 		unsigned int fn = EXTRACT_WORD(pc+2);
 		unsigned int sp = cpu.asregs.regs[1];

		MOXIE_TRACE_INSN ("jsra");
 		/* Save a slot for the static chain.  */
		sp -= 4;

 		/* Push the return address.  */
		sp -= 4;
 		wlat (scpu, opc, sp, pc + 6);
 		
 		/* Push the current frame pointer.  */
 		sp -= 4;
 		wlat (scpu, opc, sp, cpu.asregs.regs[0]);
 
 		/* Uncache the stack pointer and set the pc and $fp.  */
		cpu.asregs.regs[1] = sp;
		cpu.asregs.regs[0] = sp;
 		pc = fn - 2;
 	      }
 	      break;
 	    case 0x04: /* ret */
 	      {
 		unsigned int sp = cpu.asregs.regs[0];

		MOXIE_TRACE_INSN ("ret");
 
 		/* Pop the frame pointer.  */
 		cpu.asregs.regs[0] = rlat (scpu, opc, sp);
 		sp += 4;
 		
 		/* Pop the return address.  */
 		pc = rlat (scpu, opc, sp) - 2;
 		sp += 4;

		/* Skip over the static chain slot.  */
		sp += 4;
 
 		/* Uncache the stack pointer.  */
 		cpu.asregs.regs[1] = sp;
  	      }
  	      break;
	    case 0x05: /* add.l */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		unsigned av = cpu.asregs.regs[a];
		unsigned bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("add.l");
		cpu.asregs.regs[a] = av + bv;
	      }
	      break;
	    case 0x06: /* push */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int sp = cpu.asregs.regs[a] - 4;

		MOXIE_TRACE_INSN ("push");
		wlat (scpu, opc, sp, cpu.asregs.regs[b]);
		cpu.asregs.regs[a] = sp;
	      }
	      break;
	    case 0x07: /* pop */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int sp = cpu.asregs.regs[a];

		MOXIE_TRACE_INSN ("pop");
		cpu.asregs.regs[b] = rlat (scpu, opc, sp);
		cpu.asregs.regs[a] = sp + 4;
	      }
	      break;
	    case 0x08: /* lda.l */
	      {
		int reg = (inst >> 4) & 0xf;
		unsigned int addr = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("lda.l");
		cpu.asregs.regs[reg] = rlat (scpu, opc, addr);
		pc += 4;
	      }
	      break;
	    case 0x09: /* sta.l */
	      {
		int reg = (inst >> 4) & 0xf;
		unsigned int addr = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("sta.l");
		wlat (scpu, opc, addr, cpu.asregs.regs[reg]);
		pc += 4;
	      }
	      break;
	    case 0x0a: /* ld.l (register indirect) */
	      {
		int src  = inst & 0xf;
		int dest = (inst >> 4) & 0xf;
		int xv;

		MOXIE_TRACE_INSN ("ld.l");
		xv = cpu.asregs.regs[src];
		cpu.asregs.regs[dest] = rlat (scpu, opc, xv);
	      }
	      break;
	    case 0x0b: /* st.l */
	      {
		int dest = (inst >> 4) & 0xf;
		int val  = inst & 0xf;

		MOXIE_TRACE_INSN ("st.l");
		wlat (scpu, opc, cpu.asregs.regs[dest], cpu.asregs.regs[val]);
	      }
	      break;
	    case 0x0c: /* ldo.l */
	      {
		unsigned int addr = EXTRACT_OFFSET(pc+2);
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;

		MOXIE_TRACE_INSN ("ldo.l");
		addr += cpu.asregs.regs[b];
		cpu.asregs.regs[a] = rlat (scpu, opc, addr);
		pc += 2;
	      }
	      break;
	    case 0x0d: /* sto.l */
	      {
		unsigned int addr = EXTRACT_OFFSET(pc+2);
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;

		MOXIE_TRACE_INSN ("sto.l");
		addr += cpu.asregs.regs[a];
		wlat (scpu, opc, addr, cpu.asregs.regs[b]);
		pc += 2;
	      }
	      break;
	    case 0x0e: /* cmp */
	      {
		int a  = (inst >> 4) & 0xf;
		int b  = inst & 0xf;
		int cc = 0;
		int va = cpu.asregs.regs[a];
		int vb = cpu.asregs.regs[b]; 

		MOXIE_TRACE_INSN ("cmp");
		if (va == vb)
		  cc = CC_EQ;
		else
		  {
		    cc |= (va < vb ? CC_LT : 0);
		    cc |= (va > vb ? CC_GT : 0);
		    cc |= ((unsigned int) va < (unsigned int) vb ? CC_LTU : 0);
		    cc |= ((unsigned int) va > (unsigned int) vb ? CC_GTU : 0);
		  }

		cpu.asregs.cc = cc;
	      }
	      break;
	    case 0x0f: /* nop */
	      break;
	    case 0x10: /* sex.b */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		signed char bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("sex.b");
		cpu.asregs.regs[a] = (int) bv;
	      }
	      break;
	    case 0x11: /* sex.s */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		signed short bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("sex.s");
		cpu.asregs.regs[a] = (int) bv;
	      }
	      break;
	    case 0x12: /* zex.b */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		signed char bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("zex.b");
		cpu.asregs.regs[a] = (int) bv & 0xff;
	      }
	      break;
	    case 0x13: /* zex.s */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		signed short bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("zex.s");
		cpu.asregs.regs[a] = (int) bv & 0xffff;
	      }
	      break;
	    case 0x14: /* umul.x */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		unsigned av = cpu.asregs.regs[a];
		unsigned bv = cpu.asregs.regs[b];
		unsigned long long r = 
		  (unsigned long long) av * (unsigned long long) bv;

		MOXIE_TRACE_INSN ("umul.x");
		cpu.asregs.regs[a] = r >> 32;
	      }
	      break;
	    case 0x15: /* mul.x */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		unsigned av = cpu.asregs.regs[a];
		unsigned bv = cpu.asregs.regs[b];
		signed long long r = 
		  (signed long long) av * (signed long long) bv;

		MOXIE_TRACE_INSN ("mul.x");
		cpu.asregs.regs[a] = r >> 32;
	      }
	      break;
	    case 0x16: /* bad */
	    case 0x17: /* bad */
	    case 0x18: /* bad */
	      {
		opc = opcode;
		MOXIE_TRACE_INSN ("SIGILL0");
		sim_engine_halt (sd, NULL, NULL, pc, sim_stopped, SIM_SIGILL);
		break;
	      }
	    case 0x19: /* jsr */
	      {
		unsigned int fn = cpu.asregs.regs[(inst >> 4) & 0xf];
		unsigned int sp = cpu.asregs.regs[1];

		MOXIE_TRACE_INSN ("jsr");

 		/* Save a slot for the static chain.  */
		sp -= 4;

		/* Push the return address.  */
		sp -= 4;
		wlat (scpu, opc, sp, pc + 2);
		
		/* Push the current frame pointer.  */
		sp -= 4;
		wlat (scpu, opc, sp, cpu.asregs.regs[0]);

		/* Uncache the stack pointer and set the fp & pc.  */
		cpu.asregs.regs[1] = sp;
		cpu.asregs.regs[0] = sp;
		pc = fn - 2;
	      }
	      break;
	    case 0x1a: /* jmpa */
	      {
		unsigned int tgt = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("jmpa");
		pc = tgt - 2;
	      }
	      break;
	    case 0x1b: /* ldi.b (immediate) */
	      {
		int reg = (inst >> 4) & 0xf;
		unsigned int val = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("ldi.b");
		cpu.asregs.regs[reg] = val;
		pc += 4;
	      }
	      break;
	    case 0x1c: /* ld.b (register indirect) */
	      {
		int src  = inst & 0xf;
		int dest = (inst >> 4) & 0xf;
		int xv;

		MOXIE_TRACE_INSN ("ld.b");
		xv = cpu.asregs.regs[src];
		cpu.asregs.regs[dest] = rbat (scpu, opc, xv);
	      }
	      break;
	    case 0x1d: /* lda.b */
	      {
		int reg = (inst >> 4) & 0xf;
		unsigned int addr = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("lda.b");
		cpu.asregs.regs[reg] = rbat (scpu, opc, addr);
		pc += 4;
	      }
	      break;
	    case 0x1e: /* st.b */
	      {
		int dest = (inst >> 4) & 0xf;
		int val  = inst & 0xf;

		MOXIE_TRACE_INSN ("st.b");
		wbat (scpu, opc, cpu.asregs.regs[dest], cpu.asregs.regs[val]);
	      }
	      break;
	    case 0x1f: /* sta.b */
	      {
		int reg = (inst >> 4) & 0xf;
		unsigned int addr = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("sta.b");
		wbat (scpu, opc, addr, cpu.asregs.regs[reg]);
		pc += 4;
	      }
	      break;
	    case 0x20: /* ldi.s (immediate) */
	      {
		int reg = (inst >> 4) & 0xf;

		unsigned int val = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("ldi.s");
		cpu.asregs.regs[reg] = val;
		pc += 4;
	      }
	      break;
	    case 0x21: /* ld.s (register indirect) */
	      {
		int src  = inst & 0xf;
		int dest = (inst >> 4) & 0xf;
		int xv;

		MOXIE_TRACE_INSN ("ld.s");
		xv = cpu.asregs.regs[src];
		cpu.asregs.regs[dest] = rsat (scpu, opc, xv);
	      }
	      break;
	    case 0x22: /* lda.s */
	      {
		int reg = (inst >> 4) & 0xf;
		unsigned int addr = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("lda.s");
		cpu.asregs.regs[reg] = rsat (scpu, opc, addr);
		pc += 4;
	      }
	      break;
	    case 0x23: /* st.s */
	      {
		int dest = (inst >> 4) & 0xf;
		int val  = inst & 0xf;

		MOXIE_TRACE_INSN ("st.s");
		wsat (scpu, opc, cpu.asregs.regs[dest], cpu.asregs.regs[val]);
	      }
	      break;
	    case 0x24: /* sta.s */
	      {
		int reg = (inst >> 4) & 0xf;
		unsigned int addr = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("sta.s");
		wsat (scpu, opc, addr, cpu.asregs.regs[reg]);
		pc += 4;
	      }
	      break;
	    case 0x25: /* jmp */
	      {
		int reg = (inst >> 4) & 0xf;

		MOXIE_TRACE_INSN ("jmp");
		pc = cpu.asregs.regs[reg] - 2;
	      }
	      break;
	    case 0x26: /* and */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int av, bv;

		MOXIE_TRACE_INSN ("and");
		av = cpu.asregs.regs[a];
		bv = cpu.asregs.regs[b];
		cpu.asregs.regs[a] = av & bv;
	      }
	      break;
	    case 0x27: /* lshr */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int av = cpu.asregs.regs[a];
		int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("lshr");
		cpu.asregs.regs[a] = (unsigned) ((unsigned) av >> bv);
	      }
	      break;
	    case 0x28: /* ashl */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int av = cpu.asregs.regs[a];
		int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("ashl");
		cpu.asregs.regs[a] = av << bv;
	      }
	      break;
	    case 0x29: /* sub.l */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		unsigned av = cpu.asregs.regs[a];
		unsigned bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("sub.l");
		cpu.asregs.regs[a] = av - bv;
	      }
	      break;
	    case 0x2a: /* neg */
	      {
		int a  = (inst >> 4) & 0xf;
		int b  = inst & 0xf;
		int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("neg");
		cpu.asregs.regs[a] = - bv;
	      }
	      break;
	    case 0x2b: /* or */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int av, bv;

		MOXIE_TRACE_INSN ("or");
		av = cpu.asregs.regs[a];
		bv = cpu.asregs.regs[b];
		cpu.asregs.regs[a] = av | bv;
	      }
	      break;
	    case 0x2c: /* not */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("not");
		cpu.asregs.regs[a] = 0xffffffff ^ bv;
	      }
	      break;
	    case 0x2d: /* ashr */
	      {
		int a  = (inst >> 4) & 0xf;
		int b  = inst & 0xf;
		int av = cpu.asregs.regs[a];
		int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("ashr");
		cpu.asregs.regs[a] = av >> bv;
	      }
	      break;
	    case 0x2e: /* xor */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int av, bv;

		MOXIE_TRACE_INSN ("xor");
		av = cpu.asregs.regs[a];
		bv = cpu.asregs.regs[b];
		cpu.asregs.regs[a] = av ^ bv;
	      }
	      break;
	    case 0x2f: /* mul.l */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		unsigned av = cpu.asregs.regs[a];
		unsigned bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("mul.l");
		cpu.asregs.regs[a] = av * bv;
	      }
	      break;
	    case 0x30: /* swi */
	      {
		unsigned int inum = EXTRACT_WORD(pc+2);

		MOXIE_TRACE_INSN ("swi");
		/* Set the special registers appropriately.  */
		cpu.asregs.sregs[2] = 3; /* MOXIE_EX_SWI */
	        cpu.asregs.sregs[3] = inum;
		switch (inum)
		  {
		  case 0x1: /* SYS_exit */
		    {
		      sim_engine_halt (sd, NULL, NULL, pc, sim_exited,
				       cpu.asregs.regs[2]);
		      break;
		    }
		  case 0x2: /* SYS_open */
		    {
		      char fname[1024];
		      int mode = (int) convert_target_flags ((unsigned) cpu.asregs.regs[3]);
		      int perm = (int) cpu.asregs.regs[4];
		      int fd = open (fname, mode, perm);
		      sim_core_read_buffer (sd, scpu, read_map, fname,
					    cpu.asregs.regs[2], 1024);
		      /* FIXME - set errno */
		      cpu.asregs.regs[2] = fd;
		      break;
		    }
		  case 0x4: /* SYS_read */
		    {
		      int fd = cpu.asregs.regs[2];
		      unsigned len = (unsigned) cpu.asregs.regs[4];
		      char *buf = malloc (len);
		      cpu.asregs.regs[2] = read (fd, buf, len);
		      sim_core_write_buffer (sd, scpu, write_map, buf,
					     cpu.asregs.regs[3], len);
		      free (buf);
		      break;
		    }
		  case 0x5: /* SYS_write */
		    {
		      char *str;
		      /* String length is at 0x12($fp) */
		      unsigned count, len = (unsigned) cpu.asregs.regs[4];
		      str = malloc (len);
		      sim_core_read_buffer (sd, scpu, read_map, str,
					    cpu.asregs.regs[3], len);
		      count = write (cpu.asregs.regs[2], str, len);
		      free (str);
		      cpu.asregs.regs[2] = count;
		      break;
		    }
		  case 0xffffffff: /* Linux System Call */
		    {
		      unsigned int handler = cpu.asregs.sregs[1];
		      unsigned int sp = cpu.asregs.regs[1];

		      /* Save a slot for the static chain.  */
		      sp -= 4;

		      /* Push the return address.  */
		      sp -= 4;
		      wlat (scpu, opc, sp, pc + 6);
		
		      /* Push the current frame pointer.  */
		      sp -= 4;
		      wlat (scpu, opc, sp, cpu.asregs.regs[0]);

		      /* Uncache the stack pointer and set the fp & pc.  */
		      cpu.asregs.regs[1] = sp;
		      cpu.asregs.regs[0] = sp;
		      pc = handler - 6;
		    }
		  default:
		    break;
		  }
		pc += 4;
	      }
	      break;
	    case 0x31: /* div.l */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int av = cpu.asregs.regs[a];
		int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("div.l");
		cpu.asregs.regs[a] = av / bv;
	      }
	      break;
	    case 0x32: /* udiv.l */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		unsigned int av = cpu.asregs.regs[a];
		unsigned int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("udiv.l");
		cpu.asregs.regs[a] = (av / bv);
	      }
	      break;
	    case 0x33: /* mod.l */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		int av = cpu.asregs.regs[a];
		int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("mod.l");
		cpu.asregs.regs[a] = av % bv;
	      }
	      break;
	    case 0x34: /* umod.l */
	      {
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;
		unsigned int av = cpu.asregs.regs[a];
		unsigned int bv = cpu.asregs.regs[b];

		MOXIE_TRACE_INSN ("umod.l");
		cpu.asregs.regs[a] = (av % bv);
	      }
	      break;
	    case 0x35: /* brk */
	      MOXIE_TRACE_INSN ("brk");
	      sim_engine_halt (sd, NULL, NULL, pc, sim_stopped, SIM_SIGTRAP);
	      pc -= 2; /* Adjust pc */
	      break;
	    case 0x36: /* ldo.b */
	      {
		unsigned int addr = EXTRACT_OFFSET(pc+2);
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;

		MOXIE_TRACE_INSN ("ldo.b");
		addr += cpu.asregs.regs[b];
		cpu.asregs.regs[a] = rbat (scpu, opc, addr);
		pc += 2;
	      }
	      break;
	    case 0x37: /* sto.b */
	      {
		unsigned int addr = EXTRACT_OFFSET(pc+2);
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;

		MOXIE_TRACE_INSN ("sto.b");
		addr += cpu.asregs.regs[a];
		wbat (scpu, opc, addr, cpu.asregs.regs[b]);
		pc += 2;
	      }
	      break;
	    case 0x38: /* ldo.s */
	      {
		unsigned int addr = EXTRACT_OFFSET(pc+2);
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;

		MOXIE_TRACE_INSN ("ldo.s");
		addr += cpu.asregs.regs[b];
		cpu.asregs.regs[a] = rsat (scpu, opc, addr);
		pc += 2;
	      }
	      break;
	    case 0x39: /* sto.s */
	      {
		unsigned int addr = EXTRACT_OFFSET(pc+2);
		int a = (inst >> 4) & 0xf;
		int b = inst & 0xf;

		MOXIE_TRACE_INSN ("sto.s");
		addr += cpu.asregs.regs[a];
		wsat (scpu, opc, addr, cpu.asregs.regs[b]);
		pc += 2;
	      }
	      break;
	    default:
	      opc = opcode;
	      MOXIE_TRACE_INSN ("SIGILL1");
	      sim_engine_halt (sd, NULL, NULL, pc, sim_stopped, SIM_SIGILL);
	      break;
	    }
	}

      cpu.asregs.insts++;
      pc += 2;
      cpu.asregs.regs[PC_REGNO] = pc;
    } while (1);
}

static int
moxie_reg_store (SIM_CPU *scpu, int rn, unsigned char *memory, int length)
{
  if (rn < NUM_MOXIE_REGS && rn >= 0)
    {
      if (length == 4)
	{
	  long ival;
	  
	  /* misalignment safe */
	  ival = moxie_extract_unsigned_integer (memory, 4);
	  cpu.asints[rn] = ival;
	}

      return 4;
    }
  else
    return 0;
}

static int
moxie_reg_fetch (SIM_CPU *scpu, int rn, unsigned char *memory, int length)
{
  if (rn < NUM_MOXIE_REGS && rn >= 0)
    {
      if (length == 4)
	{
	  long ival = cpu.asints[rn];

	  /* misalignment-safe */
	  moxie_store_unsigned_integer (memory, 4, ival);
	}
      
      return 4;
    }
  else
    return 0;
}

static sim_cia
moxie_pc_get (sim_cpu *cpu)
{
  return cpu->registers[PCIDX];
}

static void
moxie_pc_set (sim_cpu *cpu, sim_cia pc)
{
  cpu->registers[PCIDX] = pc;
}

static void
free_state (SIM_DESC sd)
{
  if (STATE_MODULES (sd) != NULL)
    sim_module_uninstall (sd);
  sim_cpu_free_all (sd);
  sim_state_free (sd);
}

SIM_DESC
sim_open (SIM_OPEN_KIND kind, host_callback *cb,
	  struct bfd *abfd, char * const *argv)
{
  int i;
  SIM_DESC sd = sim_state_alloc (kind, cb);
  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);

  /* The cpu data is kept in a separately allocated chunk of memory.  */
  if (sim_cpu_alloc_all (sd, 1, /*cgen_cpu_max_extra_bytes ()*/0) != SIM_RC_OK)
    {
      free_state (sd);
      return 0;
    }

  STATE_WATCHPOINTS (sd)->pc = &cpu.asregs.regs[PC_REGNO];
  STATE_WATCHPOINTS (sd)->sizeof_pc = sizeof (word);

  if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
    {
      free_state (sd);
      return 0;
    }

  /* The parser will print an error message for us, so we silently return.  */
  if (sim_parse_args (sd, argv) != SIM_RC_OK)
    {
      free_state (sd);
      return 0;
    }

  sim_do_command(sd," memory region 0x00000000,0x4000000") ; 
  sim_do_command(sd," memory region 0xE0000000,0x10000") ; 

  /* Check for/establish the a reference program image.  */
  if (sim_analyze_program (sd,
			   (STATE_PROG_ARGV (sd) != NULL
			    ? *STATE_PROG_ARGV (sd)
			    : NULL), abfd) != SIM_RC_OK)
    {
      free_state (sd);
      return 0;
    }

  /* Configure/verify the target byte order and other runtime
     configuration options.  */
  if (sim_config (sd) != SIM_RC_OK)
    {
      sim_module_uninstall (sd);
      return 0;
    }

  if (sim_post_argv_init (sd) != SIM_RC_OK)
    {
      /* Uninstall the modules to avoid memory leaks,
	 file descriptor leaks, etc.  */
      sim_module_uninstall (sd);
      return 0;
    }

  /* CPU specific initialization.  */
  for (i = 0; i < MAX_NR_PROCESSORS; ++i)
    {
      SIM_CPU *cpu = STATE_CPU (sd, i);

      CPU_REG_FETCH (cpu) = moxie_reg_fetch;
      CPU_REG_STORE (cpu) = moxie_reg_store;
      CPU_PC_FETCH (cpu) = moxie_pc_get;
      CPU_PC_STORE (cpu) = moxie_pc_set;

      set_initial_gprs ();	/* Reset the GPR registers.  */
    }

  return sd;
}

/* Load the device tree blob.  */

static void
load_dtb (SIM_DESC sd, const char *filename)
{
  int size = 0;
  FILE *f = fopen (filename, "rb");
  char *buf;
  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */ 

  /* Don't warn as the sim works fine w/out a device tree.  */
  if (f == NULL)
    return;
  fseek (f, 0, SEEK_END);
  size = ftell(f);
  fseek (f, 0, SEEK_SET);
  buf = alloca (size);
  if (size != fread (buf, 1, size, f))
    {
      sim_io_eprintf (sd, "ERROR: error reading ``%s''.\n", filename);
      fclose (f);
      return;
    }
  sim_core_write_buffer (sd, scpu, write_map, buf, 0xE0000000, size);
  cpu.asregs.sregs[9] = 0xE0000000;
  fclose (f);
}

SIM_RC
sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd,
		     char * const *argv, char * const *env)
{
  char ** avp;
  int l, argc, i, tp;
  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */

  if (prog_bfd != NULL)
    cpu.asregs.regs[PC_REGNO] = bfd_get_start_address (prog_bfd);

  /* Copy args into target memory.  */
  avp = argv;
  for (argc = 0; avp && *avp; avp++)
    argc++;

  /* Target memory looks like this:
     0x00000000 zero word
     0x00000004 argc word
     0x00000008 start of argv
     .
     0x0000???? end of argv
     0x0000???? zero word 
     0x0000???? start of data pointed to by argv  */

  wlat (scpu, 0, 0, 0);
  wlat (scpu, 0, 4, argc);

  /* tp is the offset of our first argv data.  */
  tp = 4 + 4 + argc * 4 + 4;

  for (i = 0; i < argc; i++)
    {
      /* Set the argv value.  */
      wlat (scpu, 0, 4 + 4 + i * 4, tp);

      /* Store the string.  */
      sim_core_write_buffer (sd, scpu, write_map, argv[i],
			     tp, strlen(argv[i])+1);
      tp += strlen (argv[i]) + 1;
    }

  wlat (scpu, 0, 4 + 4 + i * 4, 0);

  load_dtb (sd, DTB);

  return SIM_RC_OK;
}
