/* Simulator for Atmel's AVR core.
   Copyright (C) 2009-2016 Free Software Foundation, Inc.
   Written by Tristan Gingold, AdaCore.

   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"

#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "bfd.h"
#include "libiberty.h"
#include "gdb/remote-sim.h"

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

/* As AVR is a 8/16 bits processor, define handy types.  */
typedef unsigned short int word;
typedef signed short int sword;
typedef unsigned char byte;
typedef signed char sbyte;

/* Max size of I space (which is always flash on avr).  */
#define MAX_AVR_FLASH (128 * 1024)
#define PC_MASK (MAX_AVR_FLASH - 1)

/* Mac size of D space.  */
#define MAX_AVR_SRAM (64 * 1024)
#define SRAM_MASK (MAX_AVR_SRAM - 1)

/* D space offset in ELF file.  */
#define SRAM_VADDR 0x800000

/* Simulator specific ports.  */
#define STDIO_PORT	0x52
#define EXIT_PORT	0x4F
#define ABORT_PORT	0x49

/* GDB defined register numbers.  */
#define AVR_SREG_REGNUM  32
#define AVR_SP_REGNUM    33
#define AVR_PC_REGNUM    34

/* Memory mapped registers.  */
#define SREG	0x5F
#define REG_SP	0x5D
#define EIND	0x5C
#define RAMPZ	0x5B

#define REGX 0x1a
#define REGY 0x1c
#define REGZ 0x1e
#define REGZ_LO 0x1e
#define REGZ_HI 0x1f

/* Sreg (status) bits.  */
#define SREG_I 0x80
#define SREG_T 0x40
#define SREG_H 0x20
#define SREG_S 0x10
#define SREG_V 0x08
#define SREG_N 0x04
#define SREG_Z 0x02
#define SREG_C 0x01

/* In order to speed up emulation we use a simple approach:
   a code is associated with each instruction.  The pre-decoding occurs
   usually once when the instruction is first seen.
   This works well because I&D spaces are separated.

   Missing opcodes: sleep, spm, wdr (as they are mmcu dependent).
*/
enum avr_opcode
  {
    /* Opcode not yet decoded.  */
    OP_unknown,
    OP_bad,

    OP_nop,

    OP_rjmp,
    OP_rcall,
    OP_ret,
    OP_reti,

    OP_break,

    OP_brbs,
    OP_brbc,

    OP_bset,
    OP_bclr,

    OP_bld,
    OP_bst,

    OP_sbrc,
    OP_sbrs,

    OP_eor,
    OP_and,
    OP_andi,
    OP_or,
    OP_ori,
    OP_com,
    OP_swap,
    OP_neg,

    OP_out,
    OP_in,
    OP_cbi,
    OP_sbi,

    OP_sbic,
    OP_sbis,

    OP_ldi,
    OP_cpse,
    OP_cp,
    OP_cpi,
    OP_cpc,
    OP_sub,
    OP_sbc,
    OP_sbiw,
    OP_adiw,
    OP_add,
    OP_adc,
    OP_subi,
    OP_sbci,
    OP_inc,
    OP_dec,
    OP_lsr,
    OP_ror,
    OP_asr,

    OP_mul,
    OP_muls,
    OP_mulsu,
    OP_fmul,
    OP_fmuls,
    OP_fmulsu,

    OP_mov,
    OP_movw,

    OP_push,
    OP_pop,

    OP_st_X,
    OP_st_dec_X,
    OP_st_X_inc,
    OP_st_Y_inc,
    OP_st_dec_Y,
    OP_st_Z_inc,
    OP_st_dec_Z,
    OP_std_Y,
    OP_std_Z,
    OP_ldd_Y,
    OP_ldd_Z,
    OP_ld_Z_inc,
    OP_ld_dec_Z,
    OP_ld_Y_inc,
    OP_ld_dec_Y,
    OP_ld_X,
    OP_ld_X_inc,
    OP_ld_dec_X,
    
    OP_lpm,
    OP_lpm_Z,
    OP_lpm_inc_Z,
    OP_elpm,
    OP_elpm_Z,
    OP_elpm_inc_Z,

    OP_ijmp,
    OP_icall,

    OP_eijmp,
    OP_eicall,

    /* 2 words opcodes.  */
#define OP_2words OP_jmp
    OP_jmp,
    OP_call,
    OP_sts,
    OP_lds
  };

struct avr_insn_cell
{
  /* The insn (16 bits).  */
  word op;

  /* Pre-decoding code.  */
  enum avr_opcode code : 8;
  /* One byte of additional information.  */
  byte r;
};

/* I&D memories.  */
/* TODO: Should be moved to SIM_CPU.  */
static struct avr_insn_cell flash[MAX_AVR_FLASH];
static byte sram[MAX_AVR_SRAM];

/* Sign extend a value.  */
static int sign_ext (word val, int nb_bits)
{
  if (val & (1 << (nb_bits - 1)))
    return val | -(1 << nb_bits);
  return val;
}

/* Insn field extractors.  */

/* Extract xxxx_xxxRx_xxxx_RRRR.  */
static inline byte get_r (word op)
{
  return (op & 0xf) | ((op >> 5) & 0x10);
}

/* Extract xxxx_xxxxx_xxxx_RRRR.  */
static inline byte get_r16 (word op)
{
  return 16 + (op & 0xf);
}

/* Extract xxxx_xxxxx_xxxx_xRRR.  */
static inline byte get_r16_23 (word op)
{
  return 16 + (op & 0x7);
}

/* Extract xxxx_xxxD_DDDD_xxxx.  */
static inline byte get_d (word op)
{
  return (op >> 4) & 0x1f;
}

/* Extract xxxx_xxxx_DDDD_xxxx.  */
static inline byte get_d16 (word op)
{
  return 16 + ((op >> 4) & 0x0f);
}

/* Extract xxxx_xxxx_xDDD_xxxx.  */
static inline byte get_d16_23 (word op)
{
  return 16 + ((op >> 4) & 0x07);
}

/* Extract xxxx_xAAx_xxxx_AAAA.  */
static inline byte get_A (word op)
{
  return (op & 0x0f) | ((op & 0x600) >> 5);
}

/* Extract xxxx_xxxx_AAAA_Axxx.  */
static inline byte get_biA (word op)
{
  return (op >> 3) & 0x1f;
}

/* Extract xxxx_KKKK_xxxx_KKKK.  */
static inline byte get_K (word op)
{
  return (op & 0xf) | ((op & 0xf00) >> 4);
}

/* Extract xxxx_xxKK_KKKK_Kxxx.  */
static inline int get_k (word op)
{
  return sign_ext ((op & 0x3f8) >> 3, 7);
}

/* Extract xxxx_xxxx_xxDD_xxxx.  */
static inline byte get_d24 (word op)
{
  return 24 + ((op >> 3) & 6);
}

/* Extract xxxx_xxxx_KKxx_KKKK.  */
static inline byte get_k6 (word op)
{
  return (op & 0xf) | ((op >> 2) & 0x30);
}
 
/* Extract xxQx_QQxx_xxxx_xQQQ.  */
static inline byte get_q (word op)
{
  return (op & 7) | ((op >> 7) & 0x18)| ((op >> 8) & 0x20);
}

/* Extract xxxx_xxxx_xxxx_xBBB.  */
static inline byte get_b (word op)
{
  return (op & 7);
}

/* AVR is little endian.  */
static inline word
read_word (unsigned int addr)
{
  return sram[addr] | (sram[addr + 1] << 8);
}

static inline void
write_word (unsigned int addr, word w)
{
  sram[addr] = w;
  sram[addr + 1] = w >> 8;
}

static inline word
read_word_post_inc (unsigned int addr)
{
  word v = read_word (addr);
  write_word (addr, v + 1);
  return v;
}

static inline word
read_word_pre_dec (unsigned int addr)
{
  word v = read_word (addr) - 1;
  write_word (addr, v);
  return v;
}

static void
update_flags_logic (byte res)
{
  sram[SREG] &= ~(SREG_S | SREG_V | SREG_N | SREG_Z);
  if (res == 0)
    sram[SREG] |= SREG_Z;
  if (res & 0x80)
    sram[SREG] |= SREG_N | SREG_S;
}

static void
update_flags_add (byte r, byte a, byte b)
{
  byte carry;

  sram[SREG] &= ~(SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
  if (r & 0x80)
    sram[SREG] |= SREG_N;
  carry = (a & b) | (a & ~r) | (b & ~r);
  if (carry & 0x08)
    sram[SREG] |= SREG_H;
  if (carry & 0x80)
    sram[SREG] |= SREG_C;
  if (((a & b & ~r) | (~a & ~b & r)) & 0x80)
    sram[SREG] |= SREG_V;
  if (!(sram[SREG] & SREG_N) ^ !(sram[SREG] & SREG_V))
    sram[SREG] |= SREG_S;
  if (r == 0)
    sram[SREG] |= SREG_Z;
}

static void update_flags_sub (byte r, byte a, byte b)
{
  byte carry;

  sram[SREG] &= ~(SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
  if (r & 0x80)
    sram[SREG] |= SREG_N;
  carry = (~a & b) | (b & r) | (r & ~a);
  if (carry & 0x08)
    sram[SREG] |= SREG_H;
  if (carry & 0x80)
    sram[SREG] |= SREG_C;
  if (((a & ~b & ~r) | (~a & b & r)) & 0x80)
    sram[SREG] |= SREG_V;
  if (!(sram[SREG] & SREG_N) ^ !(sram[SREG] & SREG_V))
    sram[SREG] |= SREG_S;
  /* Note: Z is not set.  */
}

static enum avr_opcode
decode (unsigned int pc)
{
  word op1 = flash[pc].op;

  switch ((op1 >> 12) & 0x0f)
    {
    case 0x0:
      switch ((op1 >> 10) & 0x3)
        {
        case 0x0:
          switch ((op1 >> 8) & 0x3)
            {
            case 0x0:
              if (op1 == 0)
                return OP_nop;
              break;
            case 0x1:
              return OP_movw;
            case 0x2:
              return OP_muls;
            case 0x3:
              if (op1 & 0x80)
                {
                  if (op1 & 0x08)
                    return OP_fmulsu;
                  else
                    return OP_fmuls;
                }
              else
                {
                  if (op1 & 0x08)
                    return OP_fmul;
                  else
                    return OP_mulsu;
                }
            }
          break;
        case 0x1:
          return OP_cpc;
        case 0x2:
          flash[pc].r = SREG_C;
          return OP_sbc;
        case 0x3:
          flash[pc].r = 0;
          return OP_add;
        }
      break;
    case 0x1:
      switch ((op1 >> 10) & 0x3)
        {
        case 0x0:
          return OP_cpse;
        case 0x1:
          return OP_cp;
        case 0x2:
          flash[pc].r = 0;
          return OP_sub;
        case 0x3:
          flash[pc].r = SREG_C;
          return OP_adc;
        }
      break;
    case 0x2:
      switch ((op1 >> 10) & 0x3)
        {
        case 0x0:
          return OP_and;
        case 0x1:
          return OP_eor;
        case 0x2:
          return OP_or;
        case 0x3:
          return OP_mov;
        }
      break;
    case 0x3:
      return OP_cpi;
    case 0x4:
      return OP_sbci;
    case 0x5:
      return OP_subi;
    case 0x6:
      return OP_ori;
    case 0x7:
      return OP_andi;
    case 0x8:
    case 0xa:
      if (op1 & 0x0200)
        {
          if (op1 & 0x0008)
            {
              flash[pc].r = get_q (op1);
              return OP_std_Y;
            }
          else
            {
              flash[pc].r = get_q (op1);
              return OP_std_Z;
            }
        }
      else
        {
          if (op1 & 0x0008)
            {
              flash[pc].r = get_q (op1);
              return OP_ldd_Y;
            }
          else
            {
              flash[pc].r = get_q (op1);
              return OP_ldd_Z;
            }
        }
      break;
    case 0x9: /* 9xxx */
      switch ((op1 >> 8) & 0xf)
        {
        case 0x0:
        case 0x1:
          switch ((op1 >> 0) & 0xf)
            {
            case 0x0:
              return OP_lds;
            case 0x1:
              return OP_ld_Z_inc;
            case 0x2:
              return OP_ld_dec_Z;
            case 0x4:
              return OP_lpm_Z;
            case 0x5:
              return OP_lpm_inc_Z;
            case 0x6:
              return OP_elpm_Z;
            case 0x7:
              return OP_elpm_inc_Z;
            case 0x9:
              return OP_ld_Y_inc;
            case 0xa:
              return OP_ld_dec_Y;
            case 0xc:
              return OP_ld_X;
            case 0xd:
              return OP_ld_X_inc;
            case 0xe:
              return OP_ld_dec_X;
            case 0xf:
              return OP_pop;
            }
          break;
        case 0x2:
        case 0x3:
          switch ((op1 >> 0) & 0xf)
            {
            case 0x0:
              return OP_sts;
            case 0x1:
              return OP_st_Z_inc;
            case 0x2:
              return OP_st_dec_Z;
            case 0x9:
              return OP_st_Y_inc;
            case 0xa:
              return OP_st_dec_Y;
            case 0xc:
              return OP_st_X;
            case 0xd:
              return OP_st_X_inc;
            case 0xe:
              return OP_st_dec_X;
            case 0xf:
              return OP_push;
            }
          break;
        case 0x4:
        case 0x5:
          switch (op1 & 0xf)
            {
            case 0x0:
              return OP_com;
            case 0x1:
              return OP_neg;
            case 0x2:
              return OP_swap;
            case 0x3:
              return OP_inc;
            case 0x5:
              flash[pc].r = 0x80;
              return OP_asr;
            case 0x6:
              flash[pc].r = 0;
              return OP_lsr;
            case 0x7:
              return OP_ror;
            case 0x8: /* 9[45]x8 */
              switch ((op1 >> 4) & 0x1f)
                {
                case 0x00:
                case 0x01:
                case 0x02:
                case 0x03:
                case 0x04:
                case 0x05:
                case 0x06:
                case 0x07:
                  return OP_bset;
                case 0x08:
                case 0x09:
                case 0x0a:
                case 0x0b:
                case 0x0c:
                case 0x0d:
                case 0x0e:
                case 0x0f:
                  return OP_bclr;
                case 0x10:
                  return OP_ret;
                case 0x11:
                  return OP_reti;
                case 0x19:
                  return OP_break;
                case 0x1c:
                  return OP_lpm;
                case 0x1d:
                  return OP_elpm;
                default:
                  break;
                }
              break;
            case 0x9: /* 9[45]x9 */
              switch ((op1 >> 4) & 0x1f)
                {
                case 0x00:
                  return OP_ijmp;
                case 0x01:
                  return OP_eijmp;
                case 0x10:
                  return OP_icall;
                case 0x11:
                  return OP_eicall;
                default:
                  break;
                }
              break;
            case 0xa:
              return OP_dec;
            case 0xc:
            case 0xd:
              flash[pc].r = ((op1 & 0x1f0) >> 3) | (op1 & 1);
              return OP_jmp;
            case 0xe:
            case 0xf:
              flash[pc].r = ((op1 & 0x1f0) >> 3) | (op1 & 1);
              return OP_call;
            }
          break;
        case 0x6:
          return OP_adiw;
        case 0x7:
          return OP_sbiw;
        case 0x8:
          return OP_cbi;
        case 0x9:
          return OP_sbic;
        case 0xa:
          return OP_sbi;
        case 0xb:
          return OP_sbis;
        case 0xc:
        case 0xd:
        case 0xe:
        case 0xf:
          return OP_mul;
        }
      break;
    case 0xb:
      flash[pc].r = get_A (op1);
      if (((op1 >> 11) & 1) == 0)
        return OP_in;
      else
        return OP_out;
    case 0xc:
      return OP_rjmp;
    case 0xd:
      return OP_rcall;
    case 0xe:
      return OP_ldi;
    case 0xf:
      switch ((op1 >> 9) & 7)
        {
        case 0:
        case 1:
          flash[pc].r = 1 << (op1 & 7);
          return OP_brbs;
        case 2:
        case 3:
          flash[pc].r = 1 << (op1 & 7);
          return OP_brbc;
        case 4:
          if ((op1 & 8) == 0)
            {
              flash[pc].r = 1 << (op1 & 7);
              return OP_bld;
            }
          break;
        case 5:
          if ((op1 & 8) == 0)
            {
              flash[pc].r = 1 << (op1 & 7);
              return OP_bst;
            }
          break;
        case 6:
          if ((op1 & 8) == 0)
            {
              flash[pc].r = 1 << (op1 & 7);
              return OP_sbrc;
            }
          break;
        case 7:
          if ((op1 & 8) == 0)
            {
              flash[pc].r = 1 << (op1 & 7);
              return OP_sbrs;
            }
          break;
        }
    }

  return OP_bad;
}

static void
do_call (SIM_CPU *cpu, unsigned int npc)
{
  SIM_DESC sd = CPU_STATE (cpu);
  unsigned int sp = read_word (REG_SP);

  /* Big endian!  */
  sram[sp--] = cpu->pc;
  sram[sp--] = cpu->pc >> 8;
  if (sd->avr_pc22)
    {
      sram[sp--] = cpu->pc >> 16;
      cpu->cycles++;
    }
  write_word (REG_SP, sp);
  cpu->pc = npc & PC_MASK;
  cpu->cycles += 3;
}

static int
get_insn_length (unsigned int p)
{
  if (flash[p].code == OP_unknown)
    flash[p].code = decode(p);
  if (flash[p].code >= OP_2words)
    return 2;
  else
    return 1;
}

static unsigned int
get_z (void)
{
  return (sram[RAMPZ] << 16) | (sram[REGZ_HI] << 8) | sram[REGZ_LO];
}

static unsigned char
get_lpm (unsigned int addr)
{
  word w;

  w = flash[(addr >> 1) & PC_MASK].op;
  if (addr & 1)
    w >>= 8;
  return w;
}

static void
gen_mul (SIM_CPU *cpu, unsigned int res)
{
  write_word (0, res);
  sram[SREG] &= ~(SREG_Z | SREG_C);
  if (res == 0)
    sram[SREG] |= SREG_Z;
  if (res & 0x8000)
    sram[SREG] |= SREG_C;
  cpu->cycles++;
}

static void
step_once (SIM_CPU *cpu)
{
  unsigned int ipc;

  int code;
  word op;
  byte res;
  byte r, d, vd;

 again:
  code = flash[cpu->pc].code;
  op = flash[cpu->pc].op;

#if 0
      if (tracing && code != OP_unknown)
	{
	  if (verbose > 0) {
	    int flags;
	    int i;

	    sim_cb_eprintf (callback, "R00-07:");
	    for (i = 0; i < 8; i++)
	      sim_cb_eprintf (callback, " %02x", sram[i]);
	    sim_cb_eprintf (callback, " -");
	    for (i = 8; i < 16; i++)
	      sim_cb_eprintf (callback, " %02x", sram[i]);
	    sim_cb_eprintf (callback, "  SP: %02x %02x",
                            sram[REG_SP + 1], sram[REG_SP]);
	    sim_cb_eprintf (callback, "\n");
	    sim_cb_eprintf (callback, "R16-31:");
	    for (i = 16; i < 24; i++)
	      sim_cb_eprintf (callback, " %02x", sram[i]);
	    sim_cb_eprintf (callback, " -");
	    for (i = 24; i < 32; i++)
	      sim_cb_eprintf (callback, " %02x", sram[i]);
	    sim_cb_eprintf (callback, "  ");
	    flags = sram[SREG];
	    for (i = 0; i < 8; i++)
	      sim_cb_eprintf (callback, "%c",
                              flags & (0x80 >> i) ? "ITHSVNZC"[i] : '-');
	    sim_cb_eprintf (callback, "\n");
	  }

	  if (!tracing)
	    sim_cb_eprintf (callback, "%06x: %04x\n", 2 * cpu->pc, flash[cpu->pc].op);
	  else
	    {
	      sim_cb_eprintf (callback, "pc=0x%06x insn=0x%04x code=%d r=%d\n",
                              2 * cpu->pc, flash[cpu->pc].op, code, flash[cpu->pc].r);
	      disassemble_insn (CPU_STATE (cpu), cpu->pc);
	      sim_cb_eprintf (callback, "\n");
	    }
	}
#endif

  ipc = cpu->pc;
  cpu->pc = (cpu->pc + 1) & PC_MASK;
  cpu->cycles++;

  switch (code)
    {
      case OP_unknown:
	flash[ipc].code = decode(ipc);
	cpu->pc = ipc;
	cpu->cycles--;
	goto again;

      case OP_nop:
	break;

      case OP_jmp:
	/* 2 words instruction, but we don't care about the pc.  */
	cpu->pc = ((flash[ipc].r << 16) | flash[ipc + 1].op) & PC_MASK;
	cpu->cycles += 2;
	break;

      case OP_eijmp:
	cpu->pc = ((sram[EIND] << 16) | read_word (REGZ)) & PC_MASK;
	cpu->cycles += 2;
	break;

      case OP_ijmp:
	cpu->pc = read_word (REGZ) & PC_MASK;
	cpu->cycles += 1;
	break;

      case OP_call:
	/* 2 words instruction.  */
	cpu->pc++;
	do_call (cpu, (flash[ipc].r << 16) | flash[ipc + 1].op);
	break;

      case OP_eicall:
	do_call (cpu, (sram[EIND] << 16) | read_word (REGZ));
	break;

      case OP_icall:
	do_call (cpu, read_word (REGZ));
	break;

      case OP_rcall:
	do_call (cpu, cpu->pc + sign_ext (op & 0xfff, 12));
	break;

      case OP_reti:
	sram[SREG] |= SREG_I;
	/* Fall through */
      case OP_ret:
	{
	  SIM_DESC sd = CPU_STATE (cpu);
	  unsigned int sp = read_word (REG_SP);
	  if (sd->avr_pc22)
	    {
	      cpu->pc = sram[++sp] << 16;
	      cpu->cycles++;
	    }
	  else
	    cpu->pc = 0;
	  cpu->pc |= sram[++sp] << 8;
	  cpu->pc |= sram[++sp];
	  write_word (REG_SP, sp);
	}
	cpu->cycles += 3;
	break;

      case OP_break:
	/* Stop on this address.  */
	sim_engine_halt (CPU_STATE (cpu), cpu, NULL, cpu->pc, sim_stopped, SIM_SIGTRAP);
	cpu->pc = ipc;
	break;

      case OP_bld:
	d = get_d (op);
	r = flash[ipc].r;
	if (sram[SREG] & SREG_T)
	  sram[d] |= r;
	else
	  sram[d] &= ~r;
	break;

      case OP_bst:
	if (sram[get_d (op)] & flash[ipc].r)
	  sram[SREG] |= SREG_T;
	else
	  sram[SREG] &= ~SREG_T;
	break;

      case OP_sbrc:
      case OP_sbrs:
	if (((sram[get_d (op)] & flash[ipc].r) == 0) ^ ((op & 0x0200) != 0))
	  {
	    int l = get_insn_length (cpu->pc);
	    cpu->pc += l;
	    cpu->cycles += l;
	  }
	break;

      case OP_push:
	{
	  unsigned int sp = read_word (REG_SP);
	  sram[sp--] = sram[get_d (op)];
	  write_word (REG_SP, sp);
	}
	cpu->cycles++;
	break;

      case OP_pop:
	{
	  unsigned int sp = read_word (REG_SP);
	  sram[get_d (op)] = sram[++sp];
	  write_word (REG_SP, sp);
	}
	cpu->cycles++;
	break;

      case OP_bclr:
	sram[SREG] &= ~(1 << ((op >> 4) & 0x7));
	break;

      case OP_bset:
	sram[SREG] |= 1 << ((op >> 4) & 0x7);
	break;

      case OP_rjmp:
	cpu->pc = (cpu->pc + sign_ext (op & 0xfff, 12)) & PC_MASK;
	cpu->cycles++;
	break;

      case OP_eor:
	d = get_d (op);
	res = sram[d] ^ sram[get_r (op)];
	sram[d] = res;
	update_flags_logic (res);
	break;

      case OP_and:
	d = get_d (op);
	res = sram[d] & sram[get_r (op)];
	sram[d] = res;
	update_flags_logic (res);
	break;

      case OP_andi:
	d = get_d16 (op);
	res = sram[d] & get_K (op);
	sram[d] = res;
	update_flags_logic (res);
	break;

      case OP_or:
	d = get_d (op);
	res = sram[d] | sram[get_r (op)];
	sram[d] = res;
	update_flags_logic (res);
	break;

      case OP_ori:
	d = get_d16 (op);
	res = sram[d] | get_K (op);
	sram[d] = res;
	update_flags_logic (res);
	break;

      case OP_com:
	d = get_d (op);
	res = ~sram[d];
	sram[d] = res;
	update_flags_logic (res);
	sram[SREG] |= SREG_C;
	break;

      case OP_swap:
	d = get_d (op);
	vd = sram[d];
	sram[d] = (vd >> 4) | (vd << 4);
	break;

      case OP_neg:
	d = get_d (op);
	vd = sram[d];
	res = -vd;
	sram[d] = res;
	sram[SREG] &= ~(SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
	if (res == 0)
	  sram[SREG] |= SREG_Z;
	else
	  sram[SREG] |= SREG_C;
	if (res == 0x80)
	  sram[SREG] |= SREG_V | SREG_N;
	else if (res & 0x80)
	  sram[SREG] |= SREG_N | SREG_S;
	if ((res | vd) & 0x08)
	  sram[SREG] |= SREG_H;
	break;

      case OP_inc:
	d = get_d (op);
	res = sram[d] + 1;
	sram[d] = res;
	sram[SREG] &= ~(SREG_S | SREG_V | SREG_N | SREG_Z);
	if (res == 0x80)
	  sram[SREG] |= SREG_V | SREG_N;
	else if (res & 0x80)
	  sram[SREG] |= SREG_N | SREG_S;
	else if (res == 0)
	  sram[SREG] |= SREG_Z;
	break;

      case OP_dec:
	d = get_d (op);
	res = sram[d] - 1;
	sram[d] = res;
	sram[SREG] &= ~(SREG_S | SREG_V | SREG_N | SREG_Z);
	if (res == 0x7f)
	  sram[SREG] |= SREG_V | SREG_S;
	else if (res & 0x80)
	  sram[SREG] |= SREG_N | SREG_S;
	else if (res == 0)
	  sram[SREG] |= SREG_Z;
	break;

      case OP_lsr:
      case OP_asr:
	d = get_d (op);
	vd = sram[d];
	res = (vd >> 1) | (vd & flash[ipc].r);
	sram[d] = res;
	sram[SREG] &= ~(SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
	if (vd & 1)
	  sram[SREG] |= SREG_C | SREG_S;
	if (res & 0x80)
	  sram[SREG] |= SREG_N;
	if (!(sram[SREG] & SREG_N) ^ !(sram[SREG] & SREG_C))
	  sram[SREG] |= SREG_V;
	if (res == 0)
	  sram[SREG] |= SREG_Z;
	break;

      case OP_ror:
	d = get_d (op);
	vd = sram[d];
	res = vd >> 1 | (sram[SREG] << 7);
	sram[d] = res;
	sram[SREG] &= ~(SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
	if (vd & 1)
	  sram[SREG] |= SREG_C | SREG_S;
	if (res & 0x80)
	  sram[SREG] |= SREG_N;
	if (!(sram[SREG] & SREG_N) ^ !(sram[SREG] & SREG_C))
	  sram[SREG] |= SREG_V;
	if (res == 0)
	  sram[SREG] |= SREG_Z;
	break;

      case OP_mul:
	gen_mul (cpu, (word)sram[get_r (op)] * (word)sram[get_d (op)]);
	break;

      case OP_muls:
	gen_mul (cpu, (sword)(sbyte)sram[get_r16 (op)]
		      * (sword)(sbyte)sram[get_d16 (op)]);
	break;

      case OP_mulsu:
	gen_mul (cpu, (sword)(word)sram[get_r16_23 (op)]
		      * (sword)(sbyte)sram[get_d16_23 (op)]);
	break;

      case OP_fmul:
	gen_mul (cpu, ((word)sram[get_r16_23 (op)]
		       * (word)sram[get_d16_23 (op)]) << 1);
	break;

      case OP_fmuls:
	gen_mul (cpu, ((sword)(sbyte)sram[get_r16_23 (op)]
		       * (sword)(sbyte)sram[get_d16_23 (op)]) << 1);
	break;

      case OP_fmulsu:
	gen_mul (cpu, ((sword)(word)sram[get_r16_23 (op)]
		       * (sword)(sbyte)sram[get_d16_23 (op)]) << 1);
	break;

      case OP_adc:
      case OP_add:
	r = sram[get_r (op)];
	d = get_d (op);
	vd = sram[d];
	res = r + vd + (sram[SREG] & flash[ipc].r);
	sram[d] = res;
	update_flags_add (res, vd, r);
	break;

      case OP_sub:
	d = get_d (op);
	vd = sram[d];
	r = sram[get_r (op)];
	res = vd - r;
	sram[d] = res;
	update_flags_sub (res, vd, r);
	if (res == 0)
	  sram[SREG] |= SREG_Z;
	break;

      case OP_sbc:
	{
	  byte old = sram[SREG];
	  d = get_d (op);
	  vd = sram[d];
	  r = sram[get_r (op)];
	  res = vd - r - (old & SREG_C);
	  sram[d] = res;
	  update_flags_sub (res, vd, r);
	  if (res == 0 && (old & SREG_Z))
	    sram[SREG] |= SREG_Z;
	}
	break;

      case OP_subi:
	d = get_d16 (op);
	vd = sram[d];
	r = get_K (op);
	res = vd - r;
	sram[d] = res;
	update_flags_sub (res, vd, r);
	if (res == 0)
	  sram[SREG] |= SREG_Z;
	break;

      case OP_sbci:
	{
	  byte old = sram[SREG];

	  d = get_d16 (op);
	  vd = sram[d];
	  r = get_K (op);
	  res = vd - r - (old & SREG_C);
	  sram[d] = res;
	  update_flags_sub (res, vd, r);
	  if (res == 0 && (old & SREG_Z))
	    sram[SREG] |= SREG_Z;
	}
	break;

      case OP_mov:
	sram[get_d (op)] = sram[get_r (op)];
	break;

      case OP_movw:
	d = (op & 0xf0) >> 3;
	r = (op & 0x0f) << 1;
	sram[d] = sram[r];
	sram[d + 1] = sram[r + 1];
	break;

      case OP_out:
	d = get_A (op) + 0x20;
	res = sram[get_d (op)];
	sram[d] = res;
	if (d == STDIO_PORT)
	  putchar (res);
	else if (d == EXIT_PORT)
	  sim_engine_halt (CPU_STATE (cpu), cpu, NULL, cpu->pc, sim_exited, 0);
	else if (d == ABORT_PORT)
	  sim_engine_halt (CPU_STATE (cpu), cpu, NULL, cpu->pc, sim_exited, 1);
	break;

      case OP_in:
	d = get_A (op) + 0x20;
	sram[get_d (op)] = sram[d];
	break;

      case OP_cbi:
	d = get_biA (op) + 0x20;
	sram[d] &= ~(1 << get_b(op));
	break;

      case OP_sbi:
	d = get_biA (op) + 0x20;
	sram[d] |= 1 << get_b(op);
	break;

      case OP_sbic:
	if (!(sram[get_biA (op) + 0x20] & 1 << get_b(op)))
	  {
	    int l = get_insn_length (cpu->pc);
	    cpu->pc += l;
	    cpu->cycles += l;
	  }
	break;

      case OP_sbis:
	if (sram[get_biA (op) + 0x20] & 1 << get_b(op))
	  {
	    int l = get_insn_length (cpu->pc);
	    cpu->pc += l;
	    cpu->cycles += l;
	  }
	break;

      case OP_ldi:
	res = get_K (op);
	d = get_d16 (op);
	sram[d] = res;
	break;

      case OP_lds:
	sram[get_d (op)] = sram[flash[cpu->pc].op];
	cpu->pc++;
	cpu->cycles++;
	break;

      case OP_sts:
	sram[flash[cpu->pc].op] = sram[get_d (op)];
	cpu->pc++;
	cpu->cycles++;
	break;

      case OP_cpse:
	if (sram[get_r (op)] == sram[get_d (op)])
	  {
	    int l = get_insn_length (cpu->pc);
	    cpu->pc += l;
	    cpu->cycles += l;
	  }
	break;

      case OP_cp:
	r = sram[get_r (op)];
	d = sram[get_d (op)];
	res = d - r;
	update_flags_sub (res, d, r);
	if (res == 0)
	  sram[SREG] |= SREG_Z;
	break;

      case OP_cpi:
	r = get_K (op);
	d = sram[get_d16 (op)];
	res = d - r;
	update_flags_sub (res, d, r);
	if (res == 0)
	  sram[SREG] |= SREG_Z;
	break;

      case OP_cpc:
	{
	  byte old = sram[SREG];
	  d = sram[get_d (op)];
	  r = sram[get_r (op)];
	  res = d - r - (old & SREG_C);
	  update_flags_sub (res, d, r);
	  if (res == 0 && (old & SREG_Z))
	    sram[SREG] |= SREG_Z;
	}
	break;

      case OP_brbc:
	if (!(sram[SREG] & flash[ipc].r))
	  {
	    cpu->pc = (cpu->pc + get_k (op)) & PC_MASK;
	    cpu->cycles++;
	  }
	break;

      case OP_brbs:
	if (sram[SREG] & flash[ipc].r)
	  {
	    cpu->pc = (cpu->pc + get_k (op)) & PC_MASK;
	    cpu->cycles++;
	  }
	break;

      case OP_lpm:
	sram[0] = get_lpm (read_word (REGZ));
	cpu->cycles += 2;
	break;

      case OP_lpm_Z:
	sram[get_d (op)] = get_lpm (read_word (REGZ));
	cpu->cycles += 2;
	break;

      case OP_lpm_inc_Z:
	sram[get_d (op)] = get_lpm (read_word_post_inc (REGZ));
	cpu->cycles += 2;
	break;

      case OP_elpm:
	sram[0] = get_lpm (get_z ());
	cpu->cycles += 2;
	break;

      case OP_elpm_Z:
	sram[get_d (op)] = get_lpm (get_z ());
	cpu->cycles += 2;
	break;

      case OP_elpm_inc_Z:
	{
	  unsigned int z = get_z ();

	  sram[get_d (op)] = get_lpm (z);
	  z++;
	  sram[REGZ_LO] = z;
	  sram[REGZ_HI] = z >> 8;
	  sram[RAMPZ] = z >> 16;
	}
	cpu->cycles += 2;
	break;

      case OP_ld_Z_inc:
	sram[get_d (op)] = sram[read_word_post_inc (REGZ) & SRAM_MASK];
	cpu->cycles++;
	break;

      case OP_ld_dec_Z:
	sram[get_d (op)] = sram[read_word_pre_dec (REGZ) & SRAM_MASK];
	cpu->cycles++;
	break;

      case OP_ld_X_inc:
	sram[get_d (op)] = sram[read_word_post_inc (REGX) & SRAM_MASK];
	cpu->cycles++;
	break;

      case OP_ld_dec_X:
	sram[get_d (op)] = sram[read_word_pre_dec (REGX) & SRAM_MASK];
	cpu->cycles++;
	break;

      case OP_ld_Y_inc:
	sram[get_d (op)] = sram[read_word_post_inc (REGY) & SRAM_MASK];
	cpu->cycles++;
	break;

      case OP_ld_dec_Y:
	sram[get_d (op)] = sram[read_word_pre_dec (REGY) & SRAM_MASK];
	cpu->cycles++;
	break;

      case OP_st_X:
	sram[read_word (REGX) & SRAM_MASK] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_st_X_inc:
	sram[read_word_post_inc (REGX) & SRAM_MASK] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_st_dec_X:
	sram[read_word_pre_dec (REGX) & SRAM_MASK] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_st_Z_inc:
	sram[read_word_post_inc (REGZ) & SRAM_MASK] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_st_dec_Z:
	sram[read_word_pre_dec (REGZ) & SRAM_MASK] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_st_Y_inc:
	sram[read_word_post_inc (REGY) & SRAM_MASK] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_st_dec_Y:
	sram[read_word_pre_dec (REGY) & SRAM_MASK] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_std_Y:
	sram[read_word (REGY) + flash[ipc].r] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_std_Z:
	sram[read_word (REGZ) + flash[ipc].r] = sram[get_d (op)];
	cpu->cycles++;
	break;

      case OP_ldd_Z:
	sram[get_d (op)] = sram[read_word (REGZ) + flash[ipc].r];
	cpu->cycles++;
	break;

      case OP_ldd_Y:
	sram[get_d (op)] = sram[read_word (REGY) + flash[ipc].r];
	cpu->cycles++;
	break;

      case OP_ld_X:
	sram[get_d (op)] = sram[read_word (REGX) & SRAM_MASK];
	cpu->cycles++;
	break;

      case OP_sbiw:
	{
	  word wk = get_k6 (op);
	  word wres;
	  word wr;

	  d = get_d24 (op);
	  wr = read_word (d);
	  wres = wr - wk;

	  sram[SREG] &= ~(SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
	  if (wres == 0)
	    sram[SREG] |= SREG_Z;
	  if (wres & 0x8000)
	    sram[SREG] |= SREG_N;
	  if (wres & ~wr & 0x8000)
	    sram[SREG] |= SREG_C;
	  if (~wres & wr & 0x8000)
	    sram[SREG] |= SREG_V;
	  if (((~wres & wr) ^ wres) & 0x8000)
	    sram[SREG] |= SREG_S;
	  write_word (d, wres);
	}
	cpu->cycles++;
	break;

      case OP_adiw:
	{
	  word wk = get_k6 (op);
	  word wres;
	  word wr;

	  d = get_d24 (op);
	  wr = read_word (d);
	  wres = wr + wk;

	  sram[SREG] &= ~(SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
	  if (wres == 0)
	    sram[SREG] |= SREG_Z;
	  if (wres & 0x8000)
	    sram[SREG] |= SREG_N;
	  if (~wres & wr & 0x8000)
	    sram[SREG] |= SREG_C;
	  if (wres & ~wr & 0x8000)
	    sram[SREG] |= SREG_V;
	  if (((wres & ~wr) ^ wres) & 0x8000)
	    sram[SREG] |= SREG_S;
	  write_word (d, wres);
	}
	cpu->cycles++;
	break;

      case OP_bad:
	sim_engine_halt (CPU_STATE (cpu), cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);

      default:
	sim_engine_halt (CPU_STATE (cpu), cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
      }
}

void
sim_engine_run (SIM_DESC sd,
		int next_cpu_nr, /* ignore  */
		int nr_cpus, /* ignore  */
		int siggnal) /* ignore  */
{
  SIM_CPU *cpu;

  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);

  cpu = STATE_CPU (sd, 0);

  while (1)
    {
      step_once (cpu);
      if (sim_events_tick (sd))
	sim_events_process (sd);
    }
}

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

  if (addr >= 0 && addr < SRAM_VADDR)
    {
      while (size > 0 && addr < (MAX_AVR_FLASH << 1))
	{
          word val = flash[addr >> 1].op;

          if (addr & 1)
            val = (val & 0xff) | (buffer[0] << 8);
          else
            val = (val & 0xff00) | buffer[0];

	  flash[addr >> 1].op = val;
	  flash[addr >> 1].code = OP_unknown;
	  addr++;
	  buffer++;
	  size--;
	}
      return osize - size;
    }
  else if (addr >= SRAM_VADDR && addr < SRAM_VADDR + MAX_AVR_SRAM)
    {
      addr -= SRAM_VADDR;
      if (addr + size > MAX_AVR_SRAM)
	size = MAX_AVR_SRAM - addr;
      memcpy (sram + addr, buffer, size);
      return size;
    }
  else
    return 0;
}

int
sim_read (SIM_DESC sd, SIM_ADDR addr, unsigned char *buffer, int size)
{
  int osize = size;

  if (addr >= 0 && addr < SRAM_VADDR)
    {
      while (size > 0 && addr < (MAX_AVR_FLASH << 1))
	{
          word val = flash[addr >> 1].op;

          if (addr & 1)
            val >>= 8;

          *buffer++ = val;
	  addr++;
	  size--;
	}
      return osize - size;
    }
  else if (addr >= SRAM_VADDR && addr < SRAM_VADDR + MAX_AVR_SRAM)
    {
      addr -= SRAM_VADDR;
      if (addr + size > MAX_AVR_SRAM)
	size = MAX_AVR_SRAM - addr;
      memcpy (buffer, sram + addr, size);
      return size;
    }
  else
    {
      /* Avoid errors.  */
      memset (buffer, 0, size);
      return size;
    }
}

static int
avr_reg_store (SIM_CPU *cpu, int rn, unsigned char *memory, int length)
{
  if (rn < 32 && length == 1)
    {
      sram[rn] = *memory;
      return 1;
    }
  if (rn == AVR_SREG_REGNUM && length == 1)
    {
      sram[SREG] = *memory;
      return 1;
    }
  if (rn == AVR_SP_REGNUM && length == 2)
    {
      sram[REG_SP] = memory[0];
      sram[REG_SP + 1] = memory[1];
      return 2;
    }
  if (rn == AVR_PC_REGNUM && length == 4)
    {
      cpu->pc = (memory[0] >> 1) | (memory[1] << 7)
		| (memory[2] << 15) | (memory[3] << 23);
      cpu->pc &= PC_MASK;
      return 4;
    }
  return 0;
}

static int
avr_reg_fetch (SIM_CPU *cpu, int rn, unsigned char *memory, int length)
{
  if (rn < 32 && length == 1)
    {
      *memory = sram[rn];
      return 1;
    }
  if (rn == AVR_SREG_REGNUM && length == 1)
    {
      *memory = sram[SREG];
      return 1;
    }
  if (rn == AVR_SP_REGNUM && length == 2)
    {
      memory[0] = sram[REG_SP];
      memory[1] = sram[REG_SP + 1];
      return 2;
    }
  if (rn == AVR_PC_REGNUM && length == 4)
    {
      memory[0] = cpu->pc << 1;
      memory[1] = cpu->pc >> 7;
      memory[2] = cpu->pc >> 15;
      memory[3] = cpu->pc >> 23;
      return 4;
    }
  return 0;
}

static sim_cia
avr_pc_get (sim_cpu *cpu)
{
  return cpu->pc;
}

static void
avr_pc_set (sim_cpu *cpu, sim_cia pc)
{
  cpu->pc = 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;
    }

  {
    /* XXX: Only first core gets profiled ?  */
    SIM_CPU *cpu = STATE_CPU (sd, 0);
    STATE_WATCHPOINTS (sd)->pc = &cpu->pc;
    STATE_WATCHPOINTS (sd)->sizeof_pc = sizeof (cpu->pc);
  }

  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;
    }

  /* 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) = avr_reg_fetch;
      CPU_REG_STORE (cpu) = avr_reg_store;
      CPU_PC_FETCH (cpu) = avr_pc_get;
      CPU_PC_STORE (cpu) = avr_pc_set;
    }

  /* Clear all the memory.  */
  memset (sram, 0, sizeof (sram));
  memset (flash, 0, sizeof (flash));

  return sd;
}

SIM_RC
sim_create_inferior (SIM_DESC sd, struct bfd *abfd,
		     char * const *argv, char * const *env)
{
  SIM_CPU *cpu = STATE_CPU (sd, 0);
  SIM_ADDR addr;

  /* Set the PC.  */
  if (abfd != NULL)
    addr = bfd_get_start_address (abfd);
  else
    addr = 0;
  sim_pc_set (cpu, addr);

  if (abfd != NULL)
    sd->avr_pc22 = (bfd_get_mach (abfd) >= bfd_mach_avr6);

  return SIM_RC_OK;
}
