/* insnset.c -- Instruction specific functions.

   Copyright (C) 1999 Damjan Lampret, lampret@opencores.org
                 2000-2002 Marko Mlinar, markom@opencores.org
   Copyright (C) 2008 Embecosm Limited
   Copyright (C) 2009 Jungsook yang, jungsook.yang@uci.edu
  
   Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
   Contributor Julius Baxter julius@orsoc.se  

   This file is part of OpenRISC 1000 Architectural Simulator.
  
   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/>. */

/* This program is commented throughout in a fashion suitable for processing
   with Doxygen. */


INSTRUCTION (l_add) {
  orreg_t temp1, temp2, temp3;
  int8_t temp4;
  
  temp2 = (orreg_t)PARAM2;
  temp3 = (orreg_t)PARAM1;
  temp1 = temp2 + temp3;
  SET_PARAM0 (temp1);

  /* Set overflow if two negative values gave a positive sum, or if two
     positive values gave a negative sum. Otherwise clear it */
  if ((((long int) temp2 <  0) && 
       ((long int) temp3 <  0) &&
       ((long int) temp1 >= 0)) ||
      (((long int) temp2 >= 0) && 
       ((long int) temp3 >= 0) &&
       ((long int) temp1 <  0)))
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;
    }

  /* Set the carry flag if (as unsigned values) the result is smaller than
     either operand (if it smaller than one, it will be smaller than both, so
     we need only test one). */
  if ((uorreg_t) temp1 < (uorreg_t) temp2)
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_CY;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_CY;
    }

  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
        
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for either CY or OV, and set
         appropriate AESR bit, then raise exception */
      if ((((cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_CYADDE) == SPR_AECR_CYADDE)) ||
          (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_OVADDE) == SPR_AECR_OVADDE))
          )
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_OVADDE;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_CYADDE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    } else {
      
      /* No AECR/AESR, only raise exception if OV is set */
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
        except_handle (EXCEPT_RANGE, cpu_state.pc);

    }
    
  }

  temp4 = temp1;
  if (temp4 == temp1)
    or1k_mstats.byteadd++;
}
INSTRUCTION (l_addc) {
  orreg_t temp1, temp2, temp3;
  int8_t temp4;
  int    carry_in = (cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY;

  temp2 = (orreg_t)PARAM2;
  temp3 = (orreg_t)PARAM1;
  temp1 = temp2 + temp3;

  if(carry_in)
    {
      temp1++;				/* Add in the carry bit */
    }

  SET_PARAM0(temp1);

  /* Set overflow if two negative values gave a positive sum, or if two
     positive values gave a negative sum. Otherwise clear it. There are no
     corner cases with the extra bit carried in (unlike the carry flag - see
     below). */
  if ((((long int) temp2 <  0) && 
       ((long int) temp3 <  0) &&
       ((long int) temp1 >= 0)) ||
      (((long int) temp2 >= 0) && 
       ((long int) temp3 >= 0) &&
       ((long int) temp1 <  0)))
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;
    }

  /* Set the carry flag if (as unsigned values) the result is smaller than
     either operand (if it smaller than one, it will be smaller than both, so
     we need only test one). If there is a carry in, the test should be less
     than or equal, to deal with the 0 + 0xffffffff + c = 0 case (which
     generates a carry). */
  if ((carry_in && ((uorreg_t) temp1 <= (uorreg_t) temp2)) ||
      ((uorreg_t) temp1 < (uorreg_t) temp2))
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_CY;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_CY;
    }

  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
        
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for either CY or OV, and set
         appropriate AESR bit, then raise exception */
      if ((((cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_CYADDE) == SPR_AECR_CYADDE)) ||
          (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_OVADDE) == SPR_AECR_OVADDE))
          )
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_OVADDE;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_CYADDE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    } else {
      
      /* No AECR/AESR, only raise exception if OV is set */
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV)  == SPR_SR_OV)
        except_handle (EXCEPT_RANGE, cpu_state.pc);

    }
    
  }

  temp4 = temp1;
  if (temp4 == temp1)
    or1k_mstats.byteadd++;
}
INSTRUCTION (l_swa) {
  int old_cyc = 0;
  uint32_t swa_success = 0;

  if (config.cpu.sbuf_len)
    old_cyc = runtime.sim.mem_cycles;

  if (cpu_state.loadlock_addr == dmmu_translate (PARAM0, 0)
      && cpu_state.loadlock_active) {
    swa_success = 1;
    set_mem32(PARAM0, PARAM1, &breakpoint);
    if (config.cpu.sbuf_len) {
      int t = runtime.sim.mem_cycles;
      runtime.sim.mem_cycles = old_cyc;
      sbuf_store (t - old_cyc);
    }
  }

  if (swa_success)
      cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;

  cpu_state.loadlock_active = 0;
}
INSTRUCTION (l_sw) {
  int old_cyc = 0;
  if (config.cpu.sbuf_len) old_cyc = runtime.sim.mem_cycles;
  set_mem32(PARAM0, PARAM1, &breakpoint);
  if (config.cpu.sbuf_len) {
    int t = runtime.sim.mem_cycles;
    runtime.sim.mem_cycles = old_cyc;
    sbuf_store (t - old_cyc);
  }
}
INSTRUCTION (l_sb) {
  int old_cyc = 0;
  if (config.cpu.sbuf_len) old_cyc = runtime.sim.mem_cycles;
  set_mem8(PARAM0, PARAM1, &breakpoint);
  if (config.cpu.sbuf_len) {
    int t = runtime.sim.mem_cycles;
    runtime.sim.mem_cycles = old_cyc;
    sbuf_store (t- old_cyc);
  }
}
INSTRUCTION (l_sh) {
  int old_cyc = 0;
  if (config.cpu.sbuf_len) old_cyc = runtime.sim.mem_cycles;
  set_mem16(PARAM0, PARAM1, &breakpoint);
  if (config.cpu.sbuf_len) {
    int t = runtime.sim.mem_cycles;
    runtime.sim.mem_cycles = old_cyc;
    sbuf_store (t - old_cyc);
  }
}

// XXX: All the defines are within the "instruction block" because this file
//      is "parsed" by generate.c which does not copy the other code in this
//      file to the output.

INSTRUCTION (l_sw_inc) {
/*----------------------------------------------------------------------------*/
#define GET_REG_VALUE(_reg) (cpu_state.reg[_reg])
#define SAVE_REG(_reg, value) cpu_state.reg[_reg] = value;

#define STORE_INC(mem_op, shift) \
  int old_cyc = 0;                                                             \
  uorreg_t offset = (insn >> 5) & 0x3f;                                        \
  offset |= ((insn >> 21) & 0x1f) << 6;                                        \
  if(offset & 0x400) offset |= 0xfffffc00;                                     \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t val_reg = ((insn >> 11) & 0x1f);                                    \
  if (config.cpu.sbuf_len) old_cyc = runtime.sim.mem_cycles;                   \
  orreg_t addr = (orreg_t)GET_REG_VALUE(base_reg);                             \
  uorreg_t val = GET_REG_VALUE(val_reg) >> shift;                              \
  mem_op(addr, val, &breakpoint);                                              \
  SAVE_REG(base_reg, addr + (orreg_t)offset)                                   \
  if (config.cpu.sbuf_len) {                                                   \
    int t = runtime.sim.mem_cycles;                                            \
    runtime.sim.mem_cycles = old_cyc;                                          \
    sbuf_store (t - old_cyc);                                                  \
  }

#define INC_STORE(mem_op, shift) \
  int old_cyc = 0;                                                             \
  uorreg_t offset = (insn >> 5) & 0x3f;                                        \
  offset |= ((insn >> 21) & 0x1f) << 6;                                        \
  if(offset & 0x400) offset |= 0xfffffc00;                                     \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t val_reg = ((insn >> 11) & 0x1f);                                    \
  if (config.cpu.sbuf_len) old_cyc = runtime.sim.mem_cycles;                   \
  orreg_t addr = (orreg_t)GET_REG_VALUE(base_reg) + (orreg_t)offset;           \
  uorreg_t val = GET_REG_VALUE(val_reg) >> shift;                              \
  mem_op(addr, val, &breakpoint);                                              \
  SAVE_REG(base_reg, addr)                                                     \
  if (config.cpu.sbuf_len) {                                                   \
    int t = runtime.sim.mem_cycles;                                            \
    runtime.sim.mem_cycles = old_cyc;                                          \
    sbuf_store (t - old_cyc);                                                  \
  }
/*----------------------------------------------------------------------------*/

  STORE_INC(set_mem32, 0)
}
INSTRUCTION (l_sb_inc) {
  STORE_INC(set_mem8, 0)
}
INSTRUCTION (l_sh_inc) {
  STORE_INC(set_mem16, 0)
}

INSTRUCTION (l_inc_sw) {
  INC_STORE(set_mem32, 0)
}
INSTRUCTION (l_inc_sb) {
  INC_STORE(set_mem8, 0)
}
INSTRUCTION (l_inc_sh) {
  INC_STORE(set_mem16, 0)
}

INSTRUCTION (l_sb1_inc) {
  STORE_INC(set_mem8, 8)
}
INSTRUCTION (l_sb2_inc) {
  STORE_INC(set_mem8, 16)
}
INSTRUCTION (l_sb3_inc) {
  STORE_INC(set_mem8, 24)
}
INSTRUCTION (l_sh1_inc) {
  STORE_INC(set_mem16, 16)
}

INSTRUCTION (l_inc_sb1) {
  INC_STORE(set_mem8, 8)
}
INSTRUCTION (l_inc_sb2) {
  INC_STORE(set_mem8, 16)
}
INSTRUCTION (l_inc_sb3) {
  INC_STORE(set_mem8, 24)
}
INSTRUCTION (l_inc_sh1) {
  INC_STORE(set_mem16, 16)
}

INSTRUCTION (l_lwz_inc) {
/*----------------------------------------------------------------------------*/
#define LOAD_INC(val_type, mem_op) \
  val_type val;                                                                \
  uorreg_t offset = (insn >> 5) & 0x7ff;                                       \
  if(offset & 0x00000400) offset |= 0xfffffc00;                                \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t dest_reg = ((insn >> 21) & 0x1f);                                   \
  if (config.cpu.sbuf_len) sbuf_load ();                                       \
  orreg_t addr = (orreg_t)GET_REG_VALUE(base_reg);                             \
  val = mem_op(addr, &breakpoint);                                             \
  SAVE_REG(base_reg, addr + (orreg_t)offset)                                   \
  if (!(except_pending || breakpoint))                                         \
    SAVE_REG(dest_reg, val);

#define INC_LOAD(val_type, mem_op) \
  val_type val;                                                                \
  uorreg_t offset = (insn >> 5) & 0x7ff;                                       \
  if(offset & 0x00000400) offset |= 0xfffffc00;                                \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t dest_reg = ((insn >> 21) & 0x1f);                                   \
  if (config.cpu.sbuf_len) sbuf_load ();                                       \
  orreg_t addr = (orreg_t)GET_REG_VALUE(base_reg) + (orreg_t)offset;           \
  val = mem_op(addr, &breakpoint);                                             \
  SAVE_REG(base_reg, addr)                                                     \
  if (!(except_pending || breakpoint))                                         \
    SAVE_REG(dest_reg, val);
/*----------------------------------------------------------------------------*/

  LOAD_INC(uint32_t, eval_mem32)
}
INSTRUCTION (l_lbz_inc) {
  LOAD_INC(uint8_t, eval_mem8)
}
INSTRUCTION (l_lhz_inc) {
  LOAD_INC(uint16_t, eval_mem16)
}
INSTRUCTION (l_lws_inc) {
  LOAD_INC(int32_t, eval_mem32)
}
INSTRUCTION (l_lbs_inc) {
  LOAD_INC(int8_t, eval_mem8)
}
INSTRUCTION (l_lhs_inc) {
  LOAD_INC(int16_t, eval_mem16)
}

INSTRUCTION (l_inc_lwz) {
  INC_LOAD(uint32_t, eval_mem32)
}
INSTRUCTION (l_inc_lbz) {
  INC_LOAD(uint8_t, eval_mem8)
}
INSTRUCTION (l_inc_lhz) {
  INC_LOAD(uint16_t, eval_mem16)
}
INSTRUCTION (l_inc_lws) {
  INC_LOAD(int32_t, eval_mem32)
}
INSTRUCTION (l_inc_lbs) {
  INC_LOAD(int8_t, eval_mem8)
}
INSTRUCTION (l_inc_lhs) {
  INC_LOAD(int16_t, eval_mem16)
}

INSTRUCTION (l_sw_rr) {
/*----------------------------------------------------------------------------*/
#define STORE_RR(mem_op, shift) \
  int old_cyc = 0;                                                             \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t offset_reg = ((insn >> 21) & 0x1f);                                 \
  uorreg_t val_reg = ((insn >> 11) & 0x1f);                                    \
  if (config.cpu.sbuf_len) old_cyc = runtime.sim.mem_cycles;                   \
  int addr = GET_REG_VALUE(base_reg) + GET_REG_VALUE(offset_reg);              \
  uorreg_t val = GET_REG_VALUE(val_reg) >> shift;                              \
  mem_op(addr, val, &breakpoint);                                              \
  if (config.cpu.sbuf_len) {                                                   \
    int t = runtime.sim.mem_cycles;                                            \
    runtime.sim.mem_cycles = old_cyc;                                          \
    sbuf_store (t - old_cyc);                                                  \
  }
/*----------------------------------------------------------------------------*/

  STORE_RR(set_mem32, 0)
}
INSTRUCTION (l_sh_rr) {
  STORE_RR(set_mem16, 0)
}
INSTRUCTION (l_sb_rr) {
  STORE_RR(set_mem8, 0)
}

INSTRUCTION (l_sb1_rr) {
  STORE_RR(set_mem8, 8)
}
INSTRUCTION (l_sb2_rr) {
  STORE_RR(set_mem8, 16)
}
INSTRUCTION (l_sb3_rr) {
  STORE_RR(set_mem8, 24)
}
INSTRUCTION (l_sh1_rr) {
  STORE_RR(set_mem16, 16)
}

INSTRUCTION (l_lwz_rr) {
/*----------------------------------------------------------------------------*/
#define LOAD_RR(val_type, mem_op) \
  val_type val;                                                                \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t offset_reg = ((insn >> 11) & 0x1f);                                 \
  uorreg_t dest_reg = ((insn >> 21) & 0x1f);                                   \
  if (config.cpu.sbuf_len) sbuf_load ();                                       \
  int addr = GET_REG_VALUE(base_reg) + GET_REG_VALUE(offset_reg);              \
  val = mem_op(addr, &breakpoint);                                             \
  if (!(except_pending || breakpoint))                                         \
    SAVE_REG(dest_reg, val);
/*----------------------------------------------------------------------------*/

  LOAD_RR(uint32_t, eval_mem32)
}
INSTRUCTION (l_lws_rr) {
  LOAD_RR(int32_t, eval_mem32)
}
INSTRUCTION (l_lhz_rr) {
  LOAD_RR(uint16_t, eval_mem16)
}
INSTRUCTION (l_lhs_rr) {
  LOAD_RR(int16_t, eval_mem16)
}
INSTRUCTION (l_lbz_rr) {
  LOAD_RR(uint8_t, eval_mem8)
}
INSTRUCTION (l_lbs_rr) {
  LOAD_RR(int8_t, eval_mem8)
}

INSTRUCTION (l_lwz_rr_inc) {
/*----------------------------------------------------------------------------*/
#define LOAD_RR_INC(val_type, mem_op) \
  val_type val;                                                                \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t offset_reg = ((insn >> 11) & 0x1f);                                 \
  uorreg_t dest_reg = ((insn >> 21) & 0x1f);                                   \
  if (config.cpu.sbuf_len) sbuf_load ();                                       \
  int addr = GET_REG_VALUE(base_reg);                                          \
  val = mem_op(addr, &breakpoint);                                             \
  SAVE_REG(base_reg, addr + GET_REG_VALUE(offset_reg))                         \
  if (!(except_pending || breakpoint))                                         \
    SAVE_REG(dest_reg, val);

#define INC_LOAD_RR(val_type, mem_op) \
  val_type val;                                                                \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t offset_reg = ((insn >> 11) & 0x1f);                                 \
  uorreg_t dest_reg = ((insn >> 21) & 0x1f);                                   \
  if (config.cpu.sbuf_len) sbuf_load ();                                       \
  int addr = GET_REG_VALUE(base_reg) + GET_REG_VALUE(offset_reg);              \
  val = mem_op(addr, &breakpoint);                                             \
  SAVE_REG(base_reg, addr)                                                     \
  if (!(except_pending || breakpoint))                                         \
    SAVE_REG(dest_reg, val);
/*----------------------------------------------------------------------------*/

  LOAD_RR_INC(uint32_t, eval_mem32)
}
INSTRUCTION (l_lbz_rr_inc) {
  LOAD_RR_INC(uint8_t, eval_mem8)
}
INSTRUCTION (l_lhz_rr_inc) {
  LOAD_RR_INC(uint16_t, eval_mem16)
}
INSTRUCTION (l_lws_rr_inc) {
  LOAD_RR_INC(int32_t, eval_mem32)
}
INSTRUCTION (l_lbs_rr_inc) {
  LOAD_RR_INC(uint8_t, eval_mem8)
}
INSTRUCTION (l_lhs_rr_inc) {
  LOAD_RR_INC(int16_t, eval_mem16)
}

INSTRUCTION (l_inc_lwz_rr) {
  INC_LOAD_RR(uint32_t, eval_mem32)
}
INSTRUCTION (l_inc_lbz_rr) {
  INC_LOAD_RR(uint8_t, eval_mem8)
}
INSTRUCTION (l_inc_lhz_rr) {
  INC_LOAD_RR(uint16_t, eval_mem16)
}
INSTRUCTION (l_inc_lws_rr) {
  INC_LOAD_RR(int32_t, eval_mem32)
}
INSTRUCTION (l_inc_lbs_rr) {
  INC_LOAD_RR(int8_t, eval_mem8)
}
INSTRUCTION (l_inc_lhs_rr) {
  INC_LOAD_RR(int16_t, eval_mem16)
}

INSTRUCTION (l_sw_rr_inc) {
/*----------------------------------------------------------------------------*/
#define STORE_RR_INC(mem_op, shift) \
  int old_cyc = 0;                                                             \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t offset_reg = ((insn >> 21) & 0x1f);                                 \
  uorreg_t val_reg = ((insn >> 11) & 0x1f);                                    \
  if (config.cpu.sbuf_len) old_cyc = runtime.sim.mem_cycles;                   \
  int addr = GET_REG_VALUE(base_reg);                                          \
  uorreg_t val = GET_REG_VALUE(val_reg) >> shift;                              \
  mem_op(addr, val, &breakpoint);                                              \
  SAVE_REG(base_reg, addr + GET_REG_VALUE(offset_reg))                         \
  if (config.cpu.sbuf_len) {                                                   \
    int t = runtime.sim.mem_cycles;                                            \
    runtime.sim.mem_cycles = old_cyc;                                          \
    sbuf_store (t - old_cyc);                                                  \
  }

#define INC_STORE_RR(mem_op, shift) \
  int old_cyc = 0;                                                             \
  uorreg_t base_reg = ((insn >> 16) & 0x1f);                                   \
  uorreg_t offset_reg = ((insn >> 21) & 0x1f);                                 \
  uorreg_t val_reg = ((insn >> 11) & 0x1f);                                    \
  if (config.cpu.sbuf_len) old_cyc = runtime.sim.mem_cycles;                   \
  int addr = GET_REG_VALUE(base_reg) + GET_REG_VALUE(offset_reg);              \
  uorreg_t val = GET_REG_VALUE(val_reg) >> shift;                              \
  mem_op(addr, val, &breakpoint);                                              \
  SAVE_REG(base_reg, addr)                                                     \
  if (config.cpu.sbuf_len) {                                                   \
    int t = runtime.sim.mem_cycles;                                            \
    runtime.sim.mem_cycles = old_cyc;                                          \
    sbuf_store (t - old_cyc);                                                  \
  }
/*----------------------------------------------------------------------------*/

  STORE_RR_INC(set_mem32, 0)
}
INSTRUCTION (l_sb_rr_inc) {
  STORE_RR_INC(set_mem8, 0)
}
INSTRUCTION (l_sh_rr_inc) {
  STORE_RR_INC(set_mem16, 0)
}

INSTRUCTION (l_inc_sw_rr) {
  INC_STORE_RR(set_mem32, 0)
}
INSTRUCTION (l_inc_sb_rr) {
  INC_STORE_RR(set_mem8, 0)
}
INSTRUCTION (l_inc_sh_rr) {
  INC_STORE_RR(set_mem16, 0)
}

INSTRUCTION (l_sb1_rr_inc) {
  STORE_RR_INC(set_mem8, 8)
}
INSTRUCTION (l_sb2_rr_inc) {
  STORE_RR_INC(set_mem8, 16)
}
INSTRUCTION (l_sb3_rr_inc) {
  STORE_RR_INC(set_mem8, 24)
}
INSTRUCTION (l_sh1_rr_inc) {
  STORE_RR_INC(set_mem16, 16)
}

INSTRUCTION (l_inc_sb1_rr) {
  INC_STORE_RR(set_mem8, 8)
}
INSTRUCTION (l_inc_sb2_rr) {
  INC_STORE_RR(set_mem8, 16)
}
INSTRUCTION (l_inc_sb3_rr) {
  INC_STORE_RR(set_mem8, 24)
}
INSTRUCTION (l_inc_sh1_rr) {
  INC_STORE_RR(set_mem16, 16)
}

INSTRUCTION (lp_start) {
  // PARAM0 -> id loop
  // PARAM1 -> address
  if (PARAM0 < NUM_HWLP) {
    if (cpu_state.sprs[SPR_SR] & SPR_SR_SM) {
      mtspr(SPR_HWLP_START(PARAM0), (PARAM1 << 2) + cpu_state.pc);
    } else {
      PRINTF("WARNING: trying to write SPR while SR[SUPV] is cleared.\n");
      sim_done();
    }
  } else {
    PRINTF("WARNING: trying to configure a non existing hardware loop");
    sim_done();
  }
}
INSTRUCTION (lp_end) {
  // PARAM0 -> id loop
  // PARAM1 -> address
  if (PARAM0 < NUM_HWLP) {
    if (cpu_state.sprs[SPR_SR] & SPR_SR_SM) {
      mtspr(SPR_HWLP_END(PARAM0), (PARAM1 << 2) + cpu_state.pc);
    } else {
      PRINTF("WARNING: trying to write SPR while SR[SUPV] is cleared.\n");
      sim_done();
    }
  } else {
    PRINTF("WARNING: trying to configure a non existing hardware loop");
    sim_done();
  }
}
INSTRUCTION (lp_count) {
  // PARAM0 -> id loop
  // PARAM1 -> counter
  if (PARAM0 < NUM_HWLP) {
    if (cpu_state.sprs[SPR_SR] & SPR_SR_SM) {
      mtspr(SPR_HWLP_COUNTER(PARAM0), PARAM1);
    } else {
      PRINTF("WARNING: trying to write SPR while SR[SUPV] is cleared.\n");
      sim_done();
    }
  } else {
    PRINTF("WARNING: trying to configure a non existing hardware loop");
    sim_done();
  }
}
INSTRUCTION (lp_counti) {
  // PARAM0 -> id loop
  // PARAM1 -> counter
  if (PARAM0 < NUM_HWLP) {
    if (cpu_state.sprs[SPR_SR] & SPR_SR_SM) {
      mtspr(SPR_HWLP_COUNTER(PARAM0), PARAM1);
    } else {
      PRINTF("WARNING: trying to write SPR while SR[SUPV] is cleared.\n");
      sim_done();
    }
  } else {
    PRINTF("WARNING: trying to configure a non existing hardware loop");
    sim_done();
  }
}
INSTRUCTION (lp_setupi) {
  // PARAM0 -> id loop
  // PARAM1 -> end address
  // PARAM1 -> counter
  if (PARAM0 < NUM_HWLP) {
    if (cpu_state.sprs[SPR_SR] & SPR_SR_SM) {
      mtspr(SPR_HWLP_START(PARAM0), cpu_state.pc+4);
      mtspr(SPR_HWLP_END(PARAM0), (PARAM1 << 2) + cpu_state.pc);
      mtspr(SPR_HWLP_COUNTER(PARAM0), PARAM2);
    } else {
      PRINTF("WARNING: trying to write SPR while SR[SUPV] is cleared.\n");
      sim_done();
    }
  } else {
    PRINTF("WARNING: trying to configure a non existing hardware loop");
    sim_done();
  }
}
INSTRUCTION (lp_setup) {
  // PARAM0 -> id loop
  // PARAM1 -> end address
  // PARAM1 -> counter
  if (PARAM0 < NUM_HWLP) {
    if (cpu_state.sprs[SPR_SR] & SPR_SR_SM) {
      mtspr(SPR_HWLP_START(PARAM0), cpu_state.pc+4);
      mtspr(SPR_HWLP_END(PARAM0), (PARAM1 << 2) + cpu_state.pc);
      mtspr(SPR_HWLP_COUNTER(PARAM0), PARAM2);
    } else {
      PRINTF("WARNING: trying to write SPR while SR[SUPV] is cleared.\n");
      sim_done();
    }
  } else {
    PRINTF("WARNING: trying to configure a non existing hardware loop");
    sim_done();
  }
}

INSTRUCTION (l_lwa) {
  uint32_t val;
  if (config.cpu.sbuf_len)
    sbuf_load ();
  val = eval_mem32(PARAM1, &breakpoint);
  cpu_state.loadlock_active = 1;
  cpu_state.loadlock_addr = dmmu_translate (PARAM1, 0);
  /* If eval operand produced exception don't set anything. JPB changed to
     trigger on breakpoint, as well as except_pending (seemed to be a bug). */
  if (!(except_pending || breakpoint))
    SET_PARAM0(val);
}
INSTRUCTION (l_lws) {
  uint32_t val;
  if (config.cpu.sbuf_len) sbuf_load ();
  val = eval_mem32(PARAM1, &breakpoint);
  /* If eval operand produced exception don't set anything. JPB changed to
     trigger on breakpoint, as well as except_pending (seemed to be a bug). */
  if (!(except_pending || breakpoint))
    SET_PARAM0(val);
}
INSTRUCTION (l_lwz) {
  uint32_t val;
  if (config.cpu.sbuf_len) sbuf_load ();
  val = eval_mem32(PARAM1, &breakpoint);
  /* If eval operand produced exception don't set anything. JPB changed to
     trigger on breakpoint, as well as except_pending (seemed to be a bug). */
  if (!(except_pending || breakpoint))
    SET_PARAM0(val);
}
INSTRUCTION (l_lbs) {
  int8_t val;
  if (config.cpu.sbuf_len) sbuf_load ();
  val = eval_mem8(PARAM1, &breakpoint);
  /* If eval operand produced exception don't set anything. JPB changed to
     trigger on breakpoint, as well as except_pending (seemed to be a bug). */
  if (!(except_pending || breakpoint))
    SET_PARAM0(val);
}
INSTRUCTION (l_lbz) {  
  uint8_t val;
  if (config.cpu.sbuf_len) sbuf_load ();
  val = eval_mem8(PARAM1, &breakpoint);
  /* If eval operand produced exception don't set anything. JPB changed to
     trigger on breakpoint, as well as except_pending (seemed to be a bug). */
  if (!(except_pending || breakpoint))
    SET_PARAM0(val);
}
INSTRUCTION (l_lhs) {  
  int16_t val;
  if (config.cpu.sbuf_len) sbuf_load ();
  val = eval_mem16(PARAM1, &breakpoint);
  /* If eval operand produced exception don't set anything. JPB changed to
     trigger on breakpoint, as well as except_pending (seemed to be a bug). */
  if (!(except_pending || breakpoint))
    SET_PARAM0(val);
}
INSTRUCTION (l_lhz) {  
  uint16_t val;
  if (config.cpu.sbuf_len) sbuf_load ();
  val = eval_mem16(PARAM1, &breakpoint);
  /* If eval operand produced exception don't set anything. JPB changed to
     trigger on breakpoint, as well as except_pending (seemed to be a bug). */
  if (!(except_pending || breakpoint))
    SET_PARAM0(val);
}
INSTRUCTION (l_movhi) {
  SET_PARAM0(PARAM1 << 16);
}
INSTRUCTION (l_and) {
  uorreg_t temp1;
  temp1 = PARAM1 & PARAM2;
  SET_PARAM0(temp1);
}
INSTRUCTION (l_or) {
  uorreg_t temp1;
  temp1 = PARAM1 | PARAM2;
  SET_PARAM0(temp1);
}
INSTRUCTION (l_xor) {
  /* The argument is now specified as unsigned, but historically OR1K has
     always treated the argument as signed (so l.xori rD,rA,-1 can be used in
     the absence of l.not). Use this as the default behavior. This is
     controlled from or32.c. */
  uorreg_t  temp1 = PARAM1 ^ PARAM2;
  SET_PARAM0(temp1);
}
INSTRUCTION (l_sub) {
  orreg_t temp1, temp2, temp3;

  temp3 = (orreg_t)PARAM2;
  temp2 = (orreg_t)PARAM1;
  temp1 = temp2 - temp3;
  SET_PARAM0 (temp1);

  /* Set overflow if a negative value minus a positive value gave a positive
     sum, or if a positive value minus a negative value gave a negative
     sum. Otherwise clear it */
  if ((((long int) temp2 <  0) && 
       ((long int) temp3 >= 0) &&
       ((long int) temp1 >= 0)) ||
      (((long int) temp2 >= 0) && 
       ((long int) temp3 <  0) &&
       ((long int) temp1 <  0)))
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;
    }

  /* Set the carry flag if (as unsigned values) the second operand is greater
     than the first. */
  if ((uorreg_t) temp3 > (uorreg_t) temp2)
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_CY;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_CY;
    }

  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
        
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for either CY or OV, and set
         appropriate AESR bit, then raise exception */
      if ((((cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_CYADDE) == SPR_AECR_CYADDE)) ||
          (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_OVADDE) == SPR_AECR_OVADDE))
          )
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_OVADDE;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_CYADDE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    } else {
      
      /* No AECR/AESR, only raise exception if OV is set */
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV)  == SPR_SR_OV)
        except_handle (EXCEPT_RANGE, cpu_state.pc);

    }
    
  }
}
/*int mcount = 0;*/
INSTRUCTION (l_mul) {
  orreg_t   temp0, temp1, temp2;
  LONGEST   ltemp0, ltemp1, ltemp2;

  /* Args in 32-bit */
  temp2 = (orreg_t) PARAM2;
  temp1 = (orreg_t) PARAM1;

  /* Compute initially in 64-bit */
  ltemp1 = (LONGEST) temp1;
  ltemp2 = (LONGEST) temp2;
  ltemp0 = ltemp1 * ltemp2;

  temp0  = (orreg_t) (ltemp0  & 0xffffffffLL);
  SET_PARAM0 (temp0);

  /* We have 2's complement overflow, if the result is less than the smallest
     possible 32-bit negative number, or greater than the largest possible
     32-bit positive number. */
  if ((ltemp0 < (LONGEST) INT32_MIN) || (ltemp0 > (LONGEST) INT32_MAX))
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;
    }

  /* CY flag is not touched */

  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
        
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for OV, and set
         appropriate AESR bit, then raise exception */
      if (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_OVMULE) == SPR_AECR_OVMULE))
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_OVMULE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    } else {
      
      /* No AECR/AESR, only raise exception if OV is set */
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV)  == SPR_SR_OV)
        except_handle (EXCEPT_RANGE, cpu_state.pc);

    }
    
  }

}
INSTRUCTION (l_mulu) {
  uorreg_t   temp0, temp1, temp2;
  ULONGEST  ultemp0, ultemp1, ultemp2;

  /* Args in 32-bit */
  temp2 = (uorreg_t) PARAM2;
  temp1 = (uorreg_t) PARAM1;

  /* Compute initially in 64-bit */
  ultemp1 = (ULONGEST) temp1 & 0xffffffffULL;
  ultemp2 = (ULONGEST) temp2 & 0xffffffffULL;
  ultemp0 = ultemp1 * ultemp2;

  temp0  = (uorreg_t) (ultemp0  & 0xffffffffULL);
  SET_PARAM0 (temp0);

  /* OV flag is not touched */

  /* We have 1's complement overflow, if the result is greater than the
     largest possible 32-bit unsigned number. */
  if (ultemp0 > (ULONGEST) UINT32_MAX)
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_CY;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_CY;
    }

  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
        
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for CY, and set
         appropriate AESR bit, then raise exception */
      if (((cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_CYMULE) == SPR_AECR_CYMULE))
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_CYMULE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    }

    /* mulu does not raise an exception when AECR/AESR isn't in use */
    
  }

}
INSTRUCTION (l_div) {
  orreg_t  temp3, temp2, temp1;
  
  temp3 = (orreg_t) PARAM2;
  temp2 = (orreg_t) PARAM1;
 
  /* Check for divide by zero (sets carry) */
  /* INT32_MIN / -1 should will also overflow */
  if ((0 == temp3) ||
      (0x80000000 == temp2 && 0xffffffff == temp3))
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;
    }
  else
    {
      temp1 = temp2 / temp3;
      SET_PARAM0(temp1);
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;
    }

  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
        
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for either CY or OV, and set
         appropriate AESR bit, then raise exception */
      if (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_DBZE) == SPR_AECR_DBZE))
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_DBZE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    } else {
      
      /* No AECR/AESR, only raise exception if OV is set */
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV)  == SPR_SR_OV)
        except_handle (EXCEPT_RANGE, cpu_state.pc);

    }
    
  }

}
INSTRUCTION (l_divu) {
  uorreg_t temp3, temp2, temp1;
  
  temp3 = (uorreg_t) PARAM2;
  temp2 = (uorreg_t) PARAM1;
 
 /* Check for divide by zero (sets carry) */
  if (0 == temp3)
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;
    }
  else
    {
      temp1 = temp2 / temp3;
      SET_PARAM0(temp1);
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;
    }

  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
        
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for either CY or OV, and set
         appropriate AESR bit, then raise exception */
      if (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_DBZE) == SPR_AECR_DBZE))
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_DBZE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    } else {
      
      /* No AECR/AESR, only raise exception if OV is set */
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV)  == SPR_SR_OV)
        except_handle (EXCEPT_RANGE, cpu_state.pc);

    }
    
  }

}
INSTRUCTION (l_sll) {
  uorreg_t temp1;

  temp1 = PARAM1 << PARAM2;
  SET_PARAM0(temp1);
  /* runtime.sim.cycles += 2; */
}
INSTRUCTION (l_sra) {
  orreg_t temp1;
  
  temp1 = (orreg_t)PARAM1 >> PARAM2;
  SET_PARAM0(temp1);
  /* runtime.sim.cycles += 2; */
}
INSTRUCTION (l_srl) {
  uorreg_t temp1;
  temp1 = PARAM1 >> PARAM2;
  SET_PARAM0(temp1);
  /* runtime.sim.cycles += 2; */
}
INSTRUCTION (l_ror) {
  uorreg_t temp1;
  temp1  = PARAM1 >> (PARAM2 & 0x1f);
  temp1 |= PARAM1 << (32 - (PARAM2 & 0x1f));
  SET_PARAM0(temp1);
}
INSTRUCTION (l_bf) {
  if (config.bpb.enabled) {
    int fwd = (PARAM0 >= cpu_state.pc) ? 1 : 0;
    or1k_mstats.bf[cpu_state.sprs[SPR_SR] & SPR_SR_F ? 1 : 0][fwd]++;
    bpb_update(current->insn_addr, cpu_state.sprs[SPR_SR] & SPR_SR_F ? 1 : 0);
  }
  if(cpu_state.sprs[SPR_SR] & SPR_SR_F) {
    cpu_state.pc_delay = cpu_state.pc + (orreg_t)PARAM0 * 4;
    btic_update(pcnext);
    next_delay_insn = 1;
  } else {
    btic_update(cpu_state.pc);
  }
}
INSTRUCTION (l_bnf) {
  if (config.bpb.enabled) {
    int fwd = (PARAM0 >= cpu_state.pc) ? 1 : 0;
    or1k_mstats.bnf[cpu_state.sprs[SPR_SR] & SPR_SR_F ? 0 : 1][fwd]++;
    bpb_update(current->insn_addr, cpu_state.sprs[SPR_SR] & SPR_SR_F ? 0 : 1);
  }
  if (!(cpu_state.sprs[SPR_SR] & SPR_SR_F)) {
    cpu_state.pc_delay = cpu_state.pc + (orreg_t)PARAM0 * 4;
    btic_update(pcnext);
    next_delay_insn = 1;
  } else {
    btic_update(cpu_state.pc);
  }
}
INSTRUCTION (l_j) {
/*----------------------------------------------------------------------------*/
#define SET_CARRY_OVERFLOW_FLAGS(res, res64, acc) \
     if ((res & 0x80000000) ^ (acc & 0x80000000))                              \
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;                                     \
  else                                                                         \
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;                                    \
  if (res64 & 0x100000000)                                                     \
      cpu_state.sprs[SPR_SR] |= SPR_SR_CY;                                     \
  else                                                                         \
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_CY;                                    \
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define CHECK_OVERFLOW_EXCEPTIONS \
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {                   \
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {  \
      if (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) &&                     \
          ((cpu_state.sprs[SPR_AECR] & SPR_AECR_OVMACADDE) == SPR_AECR_OVMACADDE)) { \
        cpu_state.sprs[SPR_AESR] = 0;                                          \
        if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)                 \
          cpu_state.sprs[SPR_AESR] |= SPR_AESR_OVMACADDE;                      \
        except_handle(EXCEPT_RANGE, cpu_state.pc);                             \
      }                                                                        \
    } else {                                                                   \
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)                   \
        except_handle(EXCEPT_RANGE, cpu_state.pc);                             \
    }                                                                          \
  }
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_ANY(type, num_elem, oper) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type *tmp_b = (type*)&PARAM2;                                                \
  type tmp_out[num_elem];                                                      \
  int isOper = 0;                                                              \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    if (tmp_a[i] oper tmp_b[i]) {                                              \
      tmp_out[i] = -1;                                                         \
      isOper = 1;                                                              \
    } else                                                                     \
      tmp_out[i] = 0;                                                          \
  if (isOper)                                                                  \
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;                                        \
  else                                                                         \
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;                                       \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_ALL(type, num_elem, oper) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type *tmp_b = (type*)&PARAM2;                                                \
  type tmp_out[num_elem];                                                      \
  int isOper = 1;                                                              \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    if (tmp_a[i] oper tmp_b[i])                                                \
      tmp_out[i] = -1;                                                         \
    else {                                                                     \
      tmp_out[i] = 0;                                                          \
      isOper = 0;                                                              \
    }                                                                          \
  if (isOper)                                                                  \
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;                                        \
  else                                                                         \
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;                                       \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_CMP(type, num_elem, oper) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type *tmp_b = (type*)&PARAM2;                                                \
  type tmp_out[num_elem];                                                      \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    if(tmp_a[i] oper tmp_b[i])                                                 \
      tmp_out[i] = -1;                                                         \
    else                                                                       \
      tmp_out[i] = 0;                                                          \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_CMP_SC(type, num_elem, oper) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type tmp_b = PARAM2;                                                         \
  type tmp_out[num_elem];                                                      \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    if(tmp_a[i] oper tmp_b)                                                    \
      tmp_out[i] = -1;                                                         \
    else                                                                       \
      tmp_out[i] = 0;                                                          \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_ANY_SC(type, num_elem, oper) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type tmp_b = PARAM2;                                                         \
  type tmp_out[num_elem];                                                      \
  int isOper = 0;                                                              \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    if (tmp_a[i] oper tmp_b) {                                                 \
        tmp_out[i] = -1;                                                       \
        isOper = 1;                                                            \
      } else                                                                   \
        tmp_out[i] = 0;                                                        \
  if (isOper)                                                                  \
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;                                        \
  else                                                                         \
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;                                       \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_ALL_SC(type, num_elem, oper) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type tmp_b = PARAM2;                                                         \
  type tmp_out[num_elem];                                                      \
  int isOper = 1;                                                              \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    if (tmp_a[i] oper tmp_b)                                                   \
      tmp_out[i] = -1;                                                         \
    else {                                                                     \
      tmp_out[i] = 0;                                                          \
      isOper = 0;                                                              \
    }                                                                          \
  if (isOper)                                                                  \
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;                                        \
  else                                                                         \
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;                                       \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_OP(type, num_elem, oper) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type *tmp_b = (type*)&PARAM2;                                                \
  type tmp_out[num_elem];                                                      \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    tmp_out[i] = tmp_a[i] oper tmp_b[i];                                       \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_OP_SC(type, num_elem, oper) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type tmp_b = PARAM2;                                                         \
  type tmp_out[num_elem];                                                      \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    tmp_out[i] = tmp_a[i] oper tmp_b;                                          \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_EXPR(type, num_elem, expr) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type *tmp_b = (type*)&PARAM2;                                                \
  type tmp_out[num_elem];                                                      \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    tmp_out[i] = expr;                                                         \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define LV_EXPR_SC(type, num_elem, expr) \
  type *tmp_a = (type*)&PARAM1;                                                \
  type tmp_b = PARAM2;                                                         \
  type tmp_out[num_elem];                                                      \
  int i;                                                                       \
  for (i = 0; i < num_elem; i++)                                               \
    tmp_out[i] = expr;                                                         \
  SET_PARAM0(*((uorreg_t*)tmp_out));
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define MULT_PART(cast_a, shift_a, cast_b, shift_b) \
  orreg_t rD = PARAM0;                                                         \
  orreg_t rA = PARAM1;                                                         \
  orreg_t rB = PARAM2;                                                         \
  rD =          ((cast_a)((rA >> shift_a) & 0xffff)) *                         \
       (int32_t)((cast_b)((rB >> shift_b) & 0xffff));                          \
  SET_PARAM0(rD);
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define _MAC3_PART(oper, cast_a, shift_a, cast_b, shift_b, use_carry_in) \
  orreg_t rD = PARAM0;                                                         \
  orreg_t rA = PARAM1;                                                         \
  orreg_t rB = PARAM2;                                                         \
  rA = (cast_a)((rA >> shift_a) & 0xffff);                                     \
  rB = (cast_b)((rB >> shift_b) & 0xffff);                                     \
  orreg_t acc = rD;                                                            \
  orreg_t prod = (rA * (int32_t)rB);                                           \
  int carry_in = (cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY;            \
  orreg_t res = acc oper prod;                                                 \
  uint64_t res64 = ((uint64_t)(uint32_t)acc) + ((uint64_t)(uint32_t)prod);     \
  if (carry_in && use_carry_in)                                                \
    res++;                                                                     \
  SET_PARAM0(res);                                                             \
  SET_CARRY_OVERFLOW_FLAGS(res, res64, acc)                                    \
  CHECK_OVERFLOW_EXCEPTIONS
/*----------------------------------------------------------------------------*/
#define MAC_PART(cast_a, shift_a, cast_b, shift_b) \
  _MAC3_PART(+, cast_a, shift_a, cast_b, shift_b, 0)

#define MSB_PART(cast_a, shift_a, cast_b, shift_b) \
  _MAC3_PART(-, cast_a, shift_a, cast_b, shift_b, 0)

#define MACC_PART(cast_a, shift_a, cast_b, shift_b) \
  _MAC3_PART(+, cast_a, shift_a, cast_b, shift_b, 1)
/*----------------------------------------------------------------------------*/
#define _MAC3(oper, use_carry_in) \
  orreg_t rD = PARAM0;                                                         \
  orreg_t rA = PARAM1;                                                         \
  orreg_t rB = PARAM2;                                                         \
  orreg_t acc = rD;                                                            \
  orreg_t prod = rA * rB;                                                      \
  int carry_in = (cpu_state.sprs[SPR_SR] & SPR_SR_CY) == SPR_SR_CY;            \
  orreg_t res = acc oper prod;                                                 \
  uint64_t res64 = ((uint64_t)(uint32_t)acc) + ((uint64_t)(uint32_t)prod);     \
  if (carry_in && use_carry_in)                                                \
    res++;                                                                     \
  SET_PARAM0(res);                                                             \
  SET_CARRY_OVERFLOW_FLAGS(res, res64, acc)                                    \
  CHECK_OVERFLOW_EXCEPTIONS
/*----------------------------------------------------------------------------*/

  cpu_state.pc_delay = cpu_state.pc + (orreg_t)PARAM0 * 4;
  next_delay_insn = 1;
}
INSTRUCTION (l_jal) {
  cpu_state.pc_delay = cpu_state.pc + (orreg_t)PARAM0 * 4;
  
  setsim_reg(LINK_REGNO, cpu_state.pc + 8);
  next_delay_insn = 1;
  if (config.sim.profile) {
    struct label_entry *tmp;
    if (verify_memoryarea(cpu_state.pc_delay) && (tmp = get_label (cpu_state.pc_delay)))
      fprintf (runtime.sim.fprof, "+%08llX %"PRIxADDR" %"PRIxADDR" %s\n",
               runtime.sim.cycles, cpu_state.pc + 8, cpu_state.pc_delay,
               tmp->name);
    else
      fprintf (runtime.sim.fprof, "+%08llX %"PRIxADDR" %"PRIxADDR" @%"PRIxADDR"\n",
               runtime.sim.cycles, cpu_state.pc + 8, cpu_state.pc_delay,
               cpu_state.pc_delay);
  }
}
INSTRUCTION (l_jalr) {
  /* Badly aligned destination or use of link register triggers an exception */
  uorreg_t  temp1 = PARAM0;

  if (REG_PARAM0 == LINK_REGNO)
    {
      except_handle (EXCEPT_ILLEGAL, cpu_state.pc);
    }
  else if ((temp1 & 0x3) != 0)
    {
      except_handle (EXCEPT_ALIGN, cpu_state.pc);
    }
  else
    {
      cpu_state.pc_delay = temp1;
      setsim_reg(LINK_REGNO, cpu_state.pc + 8);
      next_delay_insn = 1;
    }
}
INSTRUCTION (l_jr) {
  /* Badly aligned destination triggers an exception */
  uorreg_t  temp1 = PARAM0;

  if ((temp1 & 0x3) != 0)
    {
      except_handle (EXCEPT_ALIGN, cpu_state.pc);
    }
  else
    {
      cpu_state.pc_delay = temp1;
      next_delay_insn = 1;

      if (config.sim.profile)
	{
	  fprintf (runtime.sim.fprof, "-%08llX %"PRIxADDR"\n",
		   runtime.sim.cycles, cpu_state.pc_delay);
	}
    }
}
INSTRUCTION (l_rfe) {
  pcnext = cpu_state.sprs[SPR_EPCR_BASE];
  mtspr(SPR_SR, cpu_state.sprs[SPR_ESR_BASE]);
}
INSTRUCTION (l_nop) {
  uint32_t k = PARAM0;
  int input;
  switch (k)
    {
    case NOP_NOP:
      break;
    case NOP_EXIT:
      PRINTFQ("exit(%"PRIdREG")\n", evalsim_reg (3));
      PRINTFQ("@reset : cycles %lld, insn #%lld\n",
              runtime.sim.reset_cycles, runtime.cpu.reset_instructions);
      PRINTFQ("@exit  : cycles %lld, insn #%lld\n", runtime.sim.cycles,
              runtime.cpu.instructions);
      PRINTFQ(" diff  : cycles %lld, insn #%lld\n",
              runtime.sim.cycles - runtime.sim.reset_cycles,
              runtime.cpu.instructions - runtime.cpu.reset_instructions);
    case NOP_EXIT_SILENT:
      if (config.sim.is_library)
	{
	  runtime.cpu.halted = 1;
	  set_stall_state (1);
	}
      else
	{
	  sim_done();
	}
      break;
    case NOP_REPORT:
      PRINTF("report(0x%"PRIxREG");\n", evalsim_reg(3));
      break;
    case NOP_PUTC:		/*JPB */
      printf( "%c", (char)(evalsim_reg( 3 ) & 0xff));
      fflush( stdout );
      break;
    case NOP_GETC:		/*JPB */
      input = getchar();
      setsim_reg(3, input);
      break;
    case NOP_CNT_RESET:
      PRINTFQ("****************** counters reset ******************\n");
      PRINTFQ("cycles %lld, insn #%lld\n", runtime.sim.cycles, runtime.cpu.instructions);
      PRINTFQ("****************** counters reset ******************\n");
      runtime.sim.reset_cycles = runtime.sim.cycles;
      runtime.cpu.reset_instructions = runtime.cpu.instructions;
      break;    
    case NOP_GET_TICKS:
      cpu_state.reg[11] = runtime.sim.cycles & 0xffffffff;
      cpu_state.reg[12] = runtime.sim.cycles >> 32;
      break;
    case NOP_GET_PS:
      cpu_state.reg[11] = config.sim.clkcycle_ps;
      break;
    case NOP_TRACE_ON:
      runtime.sim.hush = 0;
      break;
    case NOP_TRACE_OFF:
      runtime.sim.hush = 1;
      break;
    case NOP_RANDOM:
      cpu_state.reg[11] = (unsigned int) (random () & 0xffffffff);
      break;
    case NOP_OR1KSIM:
      cpu_state.reg[11] = 1;
      break;
    default:
      break;
  }
}
INSTRUCTION (l_sfeq) {
  if(PARAM0 == PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sfne) {
  if(PARAM0 != PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sfgts) {
  if((orreg_t)PARAM0 > (orreg_t)PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sfges) {
  if((orreg_t)PARAM0 >= (orreg_t)PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sflts) {
  if((orreg_t)PARAM0 < (orreg_t)PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sfles) {
  if((orreg_t)PARAM0 <= (orreg_t)PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sfgtu) {
  if(PARAM0 > PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sfgeu) {
  if(PARAM0 >= PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sfltu) {
  if(PARAM0 < PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_sfleu) {
  if(PARAM0 <= PARAM1)
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
}
INSTRUCTION (l_extbs) {
  int8_t x;
  x = PARAM1;
  SET_PARAM0((orreg_t)x);
}
INSTRUCTION (l_extbz) {
  uint8_t x;
  x = PARAM1;
  SET_PARAM0((uorreg_t)x);
}
INSTRUCTION (l_exths) {
  int16_t x;
  x = PARAM1;
  SET_PARAM0((orreg_t)x);
}
INSTRUCTION (l_exthz) {
  uint16_t x;
  x = PARAM1;
  SET_PARAM0((uorreg_t)x);
}
INSTRUCTION (l_extws) {
  int32_t x;
  x = PARAM1;
  SET_PARAM0((orreg_t)x);
}
INSTRUCTION (l_extwz) {
  uint32_t x;
  x = PARAM1;
  SET_PARAM0((uorreg_t)x);
}
INSTRUCTION (l_mtspr) {
  uint16_t regno = PARAM0 | PARAM2;
  uorreg_t value = PARAM1;

  if (cpu_state.sprs[SPR_SR] & SPR_SR_SM)
    mtspr(regno, value);
  else {
    PRINTF("WARNING: trying to write SPR while SR[SUPV] is cleared.\n");
    sim_done();
  }
}
INSTRUCTION (l_mfspr) {
  uint16_t regno = PARAM1 | PARAM2;
  uorreg_t value = mfspr(regno);

  if ((cpu_state.sprs[SPR_SR] & SPR_SR_SM) ||
      // TODO: Check if this SPR should actually be allowed to be read with
      // SR's SM==0 and SUMRA==1
      (!(cpu_state.sprs[SPR_SR] & SPR_SR_SM) && 
       (cpu_state.sprs[SPR_SR] & SPR_SR_SUMRA)))
    SET_PARAM0(value);
  else
    {
      SET_PARAM0(0);
      PRINTF("WARNING: trying to read SPR while SR[SUPV] and SR[SUMRA] is cleared.\n");
      sim_done();
  }
}
INSTRUCTION (l_sys) {
  except_handle(EXCEPT_SYSCALL, cpu_state.sprs[SPR_EEAR_BASE]);
}
INSTRUCTION (l_trap) {
  /* TODO: some SR related code here! */
  except_handle(EXCEPT_TRAP, cpu_state.sprs[SPR_EEAR_BASE]);
}
INSTRUCTION (l_mac) {
  orreg_t lo, hi;
  LONGEST acc, prod, tmp;
  LONGEST x, y;

  lo = (orreg_t) cpu_state.sprs[SPR_MACLO];
  hi = (orreg_t) cpu_state.sprs[SPR_MACHI];
  x = (orreg_t) PARAM0;
  y = (orreg_t) PARAM1;

  /* current accumulator value as 64-bit signed int */
  acc = ((LONGEST)lo & 0xFFFFFFFFULL) | ((LONGEST)hi << 32);
  /* compute 32x32-bit to 64-bit product */
  prod = x * y;
  /* 64-bit addition */
  tmp = acc + prod;
  
  /* This implementation is very fast - it needs only one cycle for mac.  */
  lo = tmp & 0xFFFFFFFFULL;
  hi = (tmp >> 32) & 0xFFFFFFFFULL;
  cpu_state.sprs[SPR_MACLO] = lo;
  cpu_state.sprs[SPR_MACHI] = hi;

  /* Set overflow if two negative values gave a positive sum, or if two
     positive values gave a negative sum. Otherwise clear it */
  if (((acc <  0) && 
       (prod <  0) &&
       (tmp >= 0)) ||
      ((acc >= 0) && 
       (prod >= 0) &&
       (tmp <  0)))
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;
    }
  
  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
    
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for OV, and set
         appropriate AESR bit, then raise exception */
      if (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_OVMACADDE) == SPR_AECR_OVMACADDE))
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_OVMACADDE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    } else {
      
      /* No AECR/AESR, only raise exception if OV is set */
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV)  == SPR_SR_OV)
        except_handle (EXCEPT_RANGE, cpu_state.pc);

    }
    
  }

}
INSTRUCTION (l_msb) {
  orreg_t lo, hi;
  LONGEST acc, prod, tmp;
  LONGEST x, y;

  lo = (orreg_t) cpu_state.sprs[SPR_MACLO];
  hi = (orreg_t) cpu_state.sprs[SPR_MACHI];
  x = (orreg_t) PARAM0;
  y = (orreg_t) PARAM1;

  /* current accumulator value as 64-bit signed int */
  acc = ((LONGEST)lo & 0xFFFFFFFFULL) | ((LONGEST)hi << 32);
  /* compute 32x32-bit to 64-bit product */
  prod = x * y;
  /* 64-bit subtraction */
  tmp = acc - prod;
  
  /* This implementation is very fast - it needs only one cycle for mac.  */
  lo = tmp & 0xFFFFFFFFULL;
  hi = (tmp >> 32) & 0xFFFFFFFFULL;
  cpu_state.sprs[SPR_MACLO] = lo;
  cpu_state.sprs[SPR_MACHI] = hi;

  /* Set overflow if a negative value minus a positive value gave a positive
     sum, or if a positive value minus a negative value gave a negative
     sum. Otherwise clear it */
  if (((acc <  0) && 
       (prod >= 0) &&
       (tmp >= 0)) ||
      ((acc >= 0) && 
       (prod <  0) &&
       (tmp <  0)))
    {
      cpu_state.sprs[SPR_SR] |= SPR_SR_OV;
    }
  else
    {
      cpu_state.sprs[SPR_SR] &= ~SPR_SR_OV;
    }
  
  /* Trigger a range exception if the overflow flag is set and the SR[OVE] bit
     is set. */
  if ((cpu_state.sprs[SPR_SR] & SPR_SR_OVE) == SPR_SR_OVE) {
    
    /* Check if using precise arithmetic exceptions */
    if ((cpu_state.sprs[SPR_CPUCFGR] & SPR_CPUCFGR_AECSRP) == SPR_CPUCFGR_AECSRP) {
    
      /* If using AECR/AESR, check for OV, and set
         appropriate AESR bit, then raise exception */
      if (((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV) && ((cpu_state.sprs[SPR_AECR] & SPR_AECR_OVMACADDE) == SPR_AECR_OVMACADDE))
        {
          cpu_state.sprs[SPR_AESR] = 0;
          if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV) == SPR_SR_OV)
            cpu_state.sprs[SPR_AESR] |= SPR_AESR_OVMACADDE;
          except_handle (EXCEPT_RANGE, cpu_state.pc);
        }

    } else {
      
      /* No AECR/AESR, only raise exception if OV is set */
      if ((cpu_state.sprs[SPR_SR] & SPR_SR_OV)  == SPR_SR_OV)
        except_handle (EXCEPT_RANGE, cpu_state.pc);

    }
    
  }

}
INSTRUCTION (l_macrc) {
  orreg_t lo;
  /* No need for synchronization here -- all MAC instructions are 1 cycle long.  */
  lo =  cpu_state.sprs[SPR_MACLO];
  //PRINTF ("<%08x>\n", (unsigned long)l);
  SET_PARAM0(lo);
  cpu_state.sprs[SPR_MACLO] = 0;
  cpu_state.sprs[SPR_MACHI] = 0;
}
INSTRUCTION (l_cmov) {
  SET_PARAM0(cpu_state.sprs[SPR_SR] & SPR_SR_F ? PARAM1 : PARAM2);
}
INSTRUCTION (l_ff1) {
  SET_PARAM0(ffs(PARAM1));
}
INSTRUCTION (l_fl1) {
  orreg_t t = (orreg_t)PARAM1;

  /* Reverse the word and use ffs */
  t = (((t & 0xaaaaaaaa) >> 1) | ((t & 0x55555555) << 1));
  t = (((t & 0xcccccccc) >> 2) | ((t & 0x33333333) << 2));
  t = (((t & 0xf0f0f0f0) >> 4) | ((t & 0x0f0f0f0f) << 4));
  t = (((t & 0xff00ff00) >> 8) | ((t & 0x00ff00ff) << 8));
  t = ffs ((t >> 16) | (t << 16));
  
  SET_PARAM0 (0 == t ? t : 33 - t);
}

INSTRUCTION (l3_mac) {
  _MAC3(+, 0)
}
INSTRUCTION (l3_macc) {
  _MAC3(+, 1)
}

INSTRUCTION (l_mac_sl_sl) {
  MAC_PART(int16_t, 0, int16_t, 0)
}
INSTRUCTION (l_mac_sh_sl) {
  MAC_PART(int16_t, 16, int16_t, 0)
}
INSTRUCTION (l_mac_sh_sh) {
  MAC_PART(int16_t, 16, int16_t, 16)
}
INSTRUCTION (l_mac_zl_zl) {
  MAC_PART(uint16_t, 0, uint16_t, 0)
}
INSTRUCTION (l_mac_zh_zl) {
  MAC_PART(uint16_t, 16, uint16_t, 0)
}
INSTRUCTION (l_mac_zh_zh) {
  MAC_PART(uint16_t, 16, uint16_t, 16)
}
INSTRUCTION (l_mac_sh_zl) {
  MAC_PART(int16_t, 16, uint16_t, 0)
}

INSTRUCTION (l_macc_sl_sl) {
  MACC_PART(int16_t, 0, int16_t, 0)
}
INSTRUCTION (l_macc_sh_sl) {
  MACC_PART(int16_t, 16, int16_t, 0)
}
INSTRUCTION (l_macc_sh_sh) {
  MACC_PART(int16_t, 16, int16_t, 16)
}
INSTRUCTION (l_macc_zl_zl) {
  MACC_PART(uint16_t, 0, uint16_t, 0)
}
INSTRUCTION (l_macc_zh_zl) {
  MACC_PART(uint16_t, 16, uint16_t, 0)
}
INSTRUCTION (l_macc_zh_zh) {
  MACC_PART(uint16_t, 16, uint16_t, 16)
}
INSTRUCTION (l_macc_sh_zl) {
  MACC_PART(int16_t, 16, uint16_t, 0)
}

INSTRUCTION (l_mul_sl_sl) {
  MULT_PART(int16_t, 0, int16_t, 0)
}
INSTRUCTION (l_mul_sh_sl) {
  MULT_PART(int16_t, 16, int16_t, 0)
}
INSTRUCTION (l_mul_sh_sh) {
  MULT_PART(int16_t, 16, int16_t, 16)
}
INSTRUCTION (l_mul_zl_zl) {
  MULT_PART(uint16_t, 0, uint16_t, 0)
}
INSTRUCTION (l_mul_zh_zl) {
  MULT_PART(uint16_t, 16, uint16_t, 0)
}
INSTRUCTION (l_mul_zh_zh) {
  MULT_PART(uint16_t, 16, uint16_t, 16)
}
INSTRUCTION (l_mul_sh_zl) {
  MULT_PART(int16_t, 16, uint16_t, 0)
}

/******* Floating point instructions *******/
/* Do calculation, and update FPCSR as required */
/* Single precision */
INSTRUCTION (lf_add_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  SET_PARAM0(float32_add((unsigned int)PARAM1,(unsigned int)PARAM2));
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_div_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  SET_PARAM0(float32_div((unsigned int)PARAM1,(unsigned int)PARAM2));
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_ftoi_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  SET_PARAM0(float32_to_int32((unsigned int)PARAM1));
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_itof_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  SET_PARAM0(int32_to_float32((unsigned int)PARAM1));
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_madd_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  SET_PARAM0(float32_add((unsigned int)PARAM0, float32_mul((unsigned int)PARAM1,(unsigned int)PARAM2)));
  // Note: this ignores flags from the multiply!
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_mul_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  SET_PARAM0(float32_mul((unsigned int)PARAM1,(unsigned int)PARAM2));
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_rem_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  SET_PARAM0(float32_rem((unsigned int)PARAM1,(unsigned int)PARAM2));
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_sfeq_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  if(float32_eq((unsigned int)PARAM0, (unsigned int)PARAM1))
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_sfge_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  if((!float32_lt((unsigned int)PARAM0, (unsigned int)PARAM1) & 
      !float32_is_nan( (unsigned int)PARAM0) &
      !float32_is_nan( (unsigned int)PARAM1) ) )
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;  
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_sfgt_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  if((!float32_le((unsigned int)PARAM0, (unsigned int)PARAM1)  & 
      !float32_is_nan( (unsigned int)PARAM0) &
      !float32_is_nan( (unsigned int)PARAM1) ) )
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_sfle_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  if((float32_le((unsigned int)PARAM0, (unsigned int)PARAM1) & 
      !float32_is_nan( (unsigned int)PARAM0) &
      !float32_is_nan( (unsigned int)PARAM1) ) )    
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_sflt_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  if(( float32_lt((unsigned int)PARAM0, (unsigned int)PARAM1) & 
       !float32_is_nan( (unsigned int)PARAM0) &
       !float32_is_nan( (unsigned int)PARAM1) ) )
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_sfne_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  if(!float32_eq((unsigned int)PARAM0, (unsigned int)PARAM1))    
    cpu_state.sprs[SPR_SR] |= SPR_SR_F;
  else
    cpu_state.sprs[SPR_SR] &= ~SPR_SR_F;
  float_set_flags();
  } else l_invalid();
}
INSTRUCTION (lf_sub_s) {
  if (config.cpu.hardfloat) {
  float_set_rm();
  SET_PARAM0(float32_sub((unsigned int)PARAM1,(unsigned int)PARAM2));
  float_set_flags();
  } else l_invalid();
}

INSTRUCTION (l_min) {
  orreg_t p0, p1, res;
  p0 = (orreg_t)PARAM1;
  p1 = (orreg_t)PARAM2;
  res = p0 < p1 ? p0 : p1;
  SET_PARAM0 (res);
}
INSTRUCTION (l_minu) {
  uorreg_t p0, p1, res;
  p0 = (uorreg_t)PARAM1;
  p1 = (uorreg_t)PARAM2;
  res = p0 < p1 ? p0 : p1;
  SET_PARAM0 (res);
}
INSTRUCTION (l_max) {
  orreg_t p0, p1, res;
  p0 = (orreg_t)PARAM1;
  p1 = (orreg_t)PARAM2;
  res = p0 > p1 ? p0 : p1;
  SET_PARAM0 (res);
}
INSTRUCTION (l_maxu) {
  uorreg_t p0, p1, res;
  p0 = (uorreg_t)PARAM1;
  p1 = (uorreg_t)PARAM2;
  res = p0 > p1 ? p0 : p1;
  SET_PARAM0 (res);
}
INSTRUCTION (l_abs) {
  orreg_t p0, res;
  p0 = (orreg_t)PARAM1;
  res = p0 > 0 ? p0 : -p0;
  SET_PARAM0 (res);
}
INSTRUCTION (l_avg) {
  orreg_t p0, p1, res;
  p0 = (orreg_t)PARAM1;
  p1 = (orreg_t)PARAM2;
  res = (p0 + p1) / 2;
  SET_PARAM0 (res);
}
INSTRUCTION (l_avgu) {
  uorreg_t p0, p1, res;
  p0 = (uorreg_t)PARAM1;
  p1 = (uorreg_t)PARAM2;
  res = (p0 + p1) / 2;
  SET_PARAM0 (res);
}

INSTRUCTION (l_cnt) {
  orreg_t p0, res;
  p0 = (orreg_t)PARAM1;
  res = __builtin_popcount(p0);
  SET_PARAM0 (res);
}
INSTRUCTION (l_clb) {
  orreg_t p0, res;
  p0 = (orreg_t)PARAM1;
  res = __builtin_clrsb(p0);
  SET_PARAM0 (res);
}

INSTRUCTION (lv_all_eq_b) {
  LV_ALL(int8_t, 4, ==)
}
INSTRUCTION (lv_all_eq_h) {
  LV_ALL(int16_t, 2, ==)
}
INSTRUCTION (lv_all_ge_b) {
  LV_ALL(int8_t, 4, >=)
}
INSTRUCTION (lv_all_ge_h) {
  LV_ALL(int16_t, 2, >=)
}
INSTRUCTION (lv_all_gt_b) {
  LV_ALL(int8_t, 4, >)
}
INSTRUCTION (lv_all_gt_h) {
  LV_ALL(int16_t, 2, >)
}
INSTRUCTION (lv_all_le_b) {
  LV_ALL(int8_t, 4, <=)
}
INSTRUCTION (lv_all_le_h) {
  LV_ALL(int16_t, 2, <=)
}
INSTRUCTION (lv_all_lt_b) {
  LV_ALL(int8_t, 4, <)
}
INSTRUCTION (lv_all_lt_h) {
  LV_ALL(int16_t, 2, <)
}
INSTRUCTION (lv_all_ne_b) {
  LV_ALL(int8_t, 4, !=)
}
INSTRUCTION (lv_all_ne_h) {
  LV_ALL(int16_t, 2, !=)
}

INSTRUCTION (lv_any_eq_b) {
  LV_ANY(int8_t, 4, ==)
}
INSTRUCTION (lv_any_eq_h) {
  LV_ANY(int16_t, 2, ==)
}
INSTRUCTION (lv_any_ge_b) {
  LV_ANY(int8_t, 4, >=)
}
INSTRUCTION (lv_any_ge_h) {
  LV_ANY(int16_t, 2, >=)
}
INSTRUCTION (lv_any_gt_b) {
  LV_ANY(int8_t, 4, >)
}
INSTRUCTION (lv_any_gt_h) {
  LV_ANY(int16_t, 2, >)
}
INSTRUCTION (lv_any_le_b) {
  LV_ANY(int8_t, 4, <=)
}
INSTRUCTION (lv_any_le_h) {
  LV_ANY(int16_t, 2, <=)
}
INSTRUCTION (lv_any_lt_b) {
  LV_ANY(int8_t, 4, <)
}
INSTRUCTION (lv_any_lt_h) {
  LV_ANY(int16_t, 2, <)
}
INSTRUCTION (lv_any_ne_b) {
  LV_ANY(int8_t, 4, !=)
}
INSTRUCTION (lv_any_ne_h) {
  LV_ANY(int16_t, 2, !=)
}

INSTRUCTION (lv_all_eq_b_sc) {
  LV_ALL_SC(int8_t, 4, ==)
}
INSTRUCTION (lv_all_eq_h_sc) {
  LV_ALL_SC(int16_t, 2, ==)
}
INSTRUCTION (lv_all_ge_b_sc) {
  LV_ALL_SC(int8_t, 4, >=)
}
INSTRUCTION (lv_all_ge_h_sc) {
  LV_ALL_SC(int16_t, 2, >=)
}
INSTRUCTION (lv_all_gt_b_sc) {
  LV_ALL_SC(int8_t, 4, >)
}
INSTRUCTION (lv_all_gt_h_sc) {
  LV_ALL_SC(int16_t, 2, >)
}
INSTRUCTION (lv_all_le_b_sc) {
  LV_ALL_SC(int8_t, 4, <=)
}
INSTRUCTION (lv_all_le_h_sc) {
  LV_ALL_SC(int16_t, 2, <=)
}
INSTRUCTION (lv_all_lt_b_sc) {
  LV_ALL_SC(int8_t, 4, <)
}
INSTRUCTION (lv_all_lt_h_sc) {
  LV_ALL_SC(int16_t, 2, <)
}
INSTRUCTION (lv_all_ne_b_sc) {
  LV_ALL_SC(int8_t, 4, !=)
}
INSTRUCTION (lv_all_ne_h_sc) {
  LV_ALL_SC(int16_t, 2, !=)
}

INSTRUCTION (lv_any_eq_b_sc) {
  LV_ANY_SC(int8_t, 4, ==)
}
INSTRUCTION (lv_any_eq_h_sc) {
  LV_ANY_SC(int16_t, 2, ==)
}
INSTRUCTION (lv_any_ge_b_sc) {
  LV_ANY_SC(int8_t, 4, >=)
}
INSTRUCTION (lv_any_ge_h_sc) {
  LV_ANY_SC(int16_t, 2, >=)
}
INSTRUCTION (lv_any_gt_b_sc) {
  LV_ANY_SC(int8_t, 4, >)
}
INSTRUCTION (lv_any_gt_h_sc) {
  LV_ANY_SC(int16_t, 2, >)
}
INSTRUCTION (lv_any_le_b_sc) {
  LV_ANY_SC(int8_t, 4, <=)
}
INSTRUCTION (lv_any_le_h_sc) {
  LV_ANY_SC(int16_t, 2, <=)
}
INSTRUCTION (lv_any_lt_b_sc) {
  LV_ANY_SC(int8_t, 4, <)
}
INSTRUCTION (lv_any_lt_h_sc) {
  LV_ANY_SC(int16_t, 2, <)
}
INSTRUCTION (lv_any_ne_b_sc) {
  LV_ANY_SC(int8_t, 4, !=)
}
INSTRUCTION (lv_any_ne_h_sc) {
  LV_ANY_SC(int16_t, 2, !=)
}

INSTRUCTION (lv_cmp_eq_b_sc) {
  LV_CMP_SC(int8_t, 4, ==)
}
INSTRUCTION (lv_cmp_eq_h_sc) {
  LV_CMP_SC(int16_t, 2, ==)
}
INSTRUCTION (lv_cmp_ge_b_sc) {
  LV_CMP_SC(int8_t, 4, >=)
}
INSTRUCTION (lv_cmp_ge_h_sc) {
  LV_CMP_SC(int16_t, 2, >=)
}
INSTRUCTION (lv_cmp_gt_b_sc) {
  LV_CMP_SC(int8_t, 4, >)
}
INSTRUCTION (lv_cmp_gt_h_sc) {
  LV_CMP_SC(int16_t, 2, >)
}
INSTRUCTION (lv_cmp_le_b_sc) {
  LV_CMP_SC(int8_t, 4, <=)
}
INSTRUCTION (lv_cmp_le_h_sc) {
  LV_CMP_SC(int16_t, 2, <=)
}
INSTRUCTION (lv_cmp_lt_b_sc) {
  LV_CMP_SC(int8_t, 4, <)
}
INSTRUCTION (lv_cmp_lt_h_sc) {
  LV_CMP_SC(int16_t, 2, <)
}
INSTRUCTION (lv_cmp_ne_b_sc) {
  LV_CMP_SC(int8_t, 4, !=)
}
INSTRUCTION (lv_cmp_ne_h_sc) {
  LV_CMP_SC(int16_t, 2, !=)
}


INSTRUCTION (lv_cmp_eq_b) {
  LV_CMP(int8_t, 4, ==)
}
INSTRUCTION (lv_cmp_eq_h) {
  LV_CMP(int16_t, 2, ==)
}
INSTRUCTION (lv_cmp_ge_b) {
  LV_CMP(int8_t, 4, >=)
}
INSTRUCTION (lv_cmp_ge_h) {
  LV_CMP(int16_t, 2, >=)
}
INSTRUCTION (lv_cmp_gt_b) {
  LV_CMP(int8_t, 4, >)
}
INSTRUCTION (lv_cmp_gt_h) {
  LV_CMP(int16_t, 2, >)
}
INSTRUCTION (lv_cmp_le_b) {
  LV_CMP(int8_t, 4, <=)
}
INSTRUCTION (lv_cmp_le_h) {
  LV_CMP(int16_t, 2, <=)
}
INSTRUCTION (lv_cmp_lt_b) {
  LV_CMP(int8_t, 4, <)
}
INSTRUCTION (lv_cmp_lt_h) {
  LV_CMP(int16_t, 2, <)
}
INSTRUCTION (lv_cmp_ne_b) {
  LV_CMP(int8_t, 4, !=)
}
INSTRUCTION (lv_cmp_ne_h) {
  LV_CMP(int16_t, 2, !=)
}

INSTRUCTION (lv_all_eq_b_sci) {
  LV_ALL_SC(int8_t, 4, ==)
}
INSTRUCTION (lv_all_eq_h_sci) {
  LV_ALL_SC(int16_t, 2, ==)
}
INSTRUCTION (lv_all_ge_b_sci) {
  LV_ALL_SC(int8_t, 4, >=)
}
INSTRUCTION (lv_all_ge_h_sci) {
  LV_ALL_SC(int16_t, 2, >=)
}
INSTRUCTION (lv_all_gt_b_sci) {
  LV_ALL_SC(int8_t, 4, >)
}
INSTRUCTION (lv_all_gt_h_sci) {
  LV_ALL_SC(int16_t, 2, >)
}
INSTRUCTION (lv_all_le_b_sci) {
  LV_ALL_SC(int8_t, 4, <=)
}
INSTRUCTION (lv_all_le_h_sci) {
  LV_ALL_SC(int16_t, 2, <=)
}
INSTRUCTION (lv_all_lt_b_sci) {
  LV_ALL_SC(int8_t, 4, <)
}
INSTRUCTION (lv_all_lt_h_sci) {
  LV_ALL_SC(int16_t, 2, <)
}
INSTRUCTION (lv_all_ne_b_sci) {
  LV_ALL_SC(int8_t, 4, !=)
}
INSTRUCTION (lv_all_ne_h_sci) {
  LV_ALL_SC(int16_t, 2, !=)
}

INSTRUCTION (lv_any_eq_b_sci) {
  LV_ANY_SC(int8_t, 4, ==)
}
INSTRUCTION (lv_any_eq_h_sci) {
  LV_ANY_SC(int16_t, 2, ==)
}
INSTRUCTION (lv_any_ge_b_sci) {
  LV_ANY_SC(int8_t, 4, >=)
}
INSTRUCTION (lv_any_ge_h_sci) {
  LV_ANY_SC(int16_t, 2, >=)
}
INSTRUCTION (lv_any_gt_b_sci) {
  LV_ANY_SC(int8_t, 4, >)
}
INSTRUCTION (lv_any_gt_h_sci) {
  LV_ANY_SC(int16_t, 2, >)
}
INSTRUCTION (lv_any_le_b_sci) {
  LV_ANY_SC(int8_t, 4, <=)
}
INSTRUCTION (lv_any_le_h_sci) {
  LV_ANY_SC(int16_t, 2, <=)
}
INSTRUCTION (lv_any_lt_b_sci) {
  LV_ANY_SC(int8_t, 4, <)
}
INSTRUCTION (lv_any_lt_h_sci) {
  LV_ANY_SC(int16_t, 2, <)
}
INSTRUCTION (lv_any_ne_b_sci) {
  LV_ANY_SC(int8_t, 4, !=)
}
INSTRUCTION (lv_any_ne_h_sci) {
  LV_ANY_SC(int16_t, 2, !=)
}

INSTRUCTION (lv_cmp_eq_b_sci) {
  LV_CMP_SC(int8_t, 4, ==)
}
INSTRUCTION (lv_cmp_eq_h_sci) {
  LV_CMP_SC(int16_t, 2, ==)
}
INSTRUCTION (lv_cmp_ge_b_sci) {
  LV_CMP_SC(int8_t, 4, >=)
}
INSTRUCTION (lv_cmp_ge_h_sci) {
  LV_CMP_SC(int16_t, 2, >=)
}
INSTRUCTION (lv_cmp_gt_b_sci) {
  LV_CMP_SC(int8_t, 4, >)
}
INSTRUCTION (lv_cmp_gt_h_sci) {
  LV_CMP_SC(int16_t, 2, >)
}
INSTRUCTION (lv_cmp_le_b_sci) {
  LV_CMP_SC(int8_t, 4, <=)
}
INSTRUCTION (lv_cmp_le_h_sci) {
  LV_CMP_SC(int16_t, 2, <=)
}
INSTRUCTION (lv_cmp_lt_b_sci) {
  LV_CMP_SC(int8_t, 4, <)
}
INSTRUCTION (lv_cmp_lt_h_sci) {
  LV_CMP_SC(int16_t, 2, <)
}
INSTRUCTION (lv_cmp_ne_b_sci) {
  LV_CMP_SC(int8_t, 4, !=)
}
INSTRUCTION (lv_cmp_ne_h_sci) {
  LV_CMP_SC(int16_t, 2, !=)
}


INSTRUCTION (lv_add_b) {
  LV_OP(int8_t, 4, +)
}
INSTRUCTION (lv_add_h) {
  LV_OP(int16_t, 2, +)
}
INSTRUCTION (lv_sub_b) {
  LV_OP(int8_t, 4, -)
}
INSTRUCTION (lv_sub_h) {
  LV_OP(int16_t, 2, -)
}
INSTRUCTION (lv_avg_b) {
  LV_EXPR(int8_t, 4, ((int8_t)(tmp_a[i] + tmp_b[i])>>(int8_t)1))
}
INSTRUCTION (lv_avg_h) {
  LV_EXPR(int16_t, 2, ((int16_t)(tmp_a[i] + tmp_b[i])>>(int16_t)1))
}
INSTRUCTION (lv_min_b) {
  LV_EXPR(int8_t, 4, (tmp_a[i]>tmp_b[i] ? tmp_b[i] : tmp_a[i]))
}
INSTRUCTION (lv_min_h) {
  LV_EXPR(int16_t, 2, (tmp_a[i]>tmp_b[i] ? tmp_b[i] : tmp_a[i]))
}
INSTRUCTION (lv_max_b) {
  LV_EXPR(int8_t, 4, (tmp_a[i]>tmp_b[i] ? tmp_a[i] : tmp_b[i]))
}
INSTRUCTION (lv_max_h) {
  LV_EXPR(int16_t, 2, (tmp_a[i]>tmp_b[i] ? tmp_a[i] : tmp_b[i]))
}
INSTRUCTION (lv_srl_b) {
  LV_OP(uint8_t, 4, >>)
}
INSTRUCTION (lv_srl_h) {
  LV_OP(uint16_t, 2, >>)
}
INSTRUCTION (lv_sra_b) {
  LV_OP(int8_t, 4, >>)
}
INSTRUCTION (lv_sra_h) {
  LV_OP(int16_t, 2, >>)
}
INSTRUCTION (lv_sll_b) {
  LV_OP(uint8_t, 4, <<)
}
INSTRUCTION (lv_sll_h) {
  LV_OP(uint16_t, 2, <<)
}
INSTRUCTION (lv_mul_b) {
  LV_OP(int8_t, 4, *)
}
INSTRUCTION (lv_mul_h) {
  LV_OP(int16_t, 2, *)
}
INSTRUCTION (lv_or_b) {
  LV_OP(int8_t, 4, |)
}
INSTRUCTION (lv_or_h) {
  LV_OP(int16_t, 2, |)
}
INSTRUCTION (lv_xor_b) {
  LV_OP(int8_t, 4, ^)
}
INSTRUCTION (lv_xor_h) {
  LV_OP(int16_t, 2, ^)
}
INSTRUCTION (lv_and_b) {
  LV_OP(int8_t, 4, &)
}
INSTRUCTION (lv_and_h) {
  LV_OP(int16_t, 2, &)
}

INSTRUCTION (lv_add_b_sc) {
  LV_OP_SC(int8_t, 4, +)
}
INSTRUCTION (lv_add_h_sc) {
  LV_OP_SC(int16_t, 2, +)
}
INSTRUCTION (lv_sub_b_sc) {
  LV_OP_SC(int8_t, 4, -)
}
INSTRUCTION (lv_sub_h_sc) {
  LV_OP_SC(int16_t, 2, -)
}
INSTRUCTION (lv_avg_b_sc) {
  LV_EXPR_SC(int8_t, 4, ((int8_t)(tmp_a[i] + tmp_b)>>(int8_t)1))
}
INSTRUCTION (lv_avg_h_sc) {
  LV_EXPR_SC(int16_t, 2, ((int16_t)(tmp_a[i] + tmp_b)>>(int16_t)1))
}
INSTRUCTION (lv_min_b_sc) {
  LV_EXPR_SC(int8_t, 4, (tmp_a[i] > tmp_b ? tmp_b : tmp_a[i]))
}
INSTRUCTION (lv_min_h_sc) {
  LV_EXPR_SC(int16_t, 2, (tmp_a[i] > tmp_b ? tmp_b : tmp_a[i]))
}
INSTRUCTION (lv_max_b_sc) {
  LV_EXPR_SC(int8_t, 4, (tmp_a[i] > tmp_b ? tmp_a[i] : tmp_b))
}
INSTRUCTION (lv_max_h_sc) {
  LV_EXPR_SC(int16_t, 2, (tmp_a[i] > tmp_b ? tmp_a[i] : tmp_b))
}
INSTRUCTION (lv_srl_b_sc) {
  LV_OP_SC(uint8_t, 4, >>)
}
INSTRUCTION (lv_srl_h_sc) {
  LV_OP_SC(uint16_t, 2, >>)
}
INSTRUCTION (lv_sra_b_sc) {
  LV_OP_SC(int8_t, 4, >>)
}
INSTRUCTION (lv_sra_h_sc) {
  LV_OP_SC(int16_t, 2, >>)
}
INSTRUCTION (lv_sll_b_sc) {
  LV_OP_SC(uint8_t, 4, <<)
}
INSTRUCTION (lv_sll_h_sc) {
  LV_OP_SC(uint16_t, 2, <<)
}
INSTRUCTION (lv_mul_b_sc) {
  LV_OP_SC(int8_t, 4, *)
}
INSTRUCTION (lv_mul_h_sc) {
  LV_OP_SC(int16_t, 2, *)
}
INSTRUCTION (lv_or_b_sc) {
  LV_OP_SC(int8_t, 4, |)
}
INSTRUCTION (lv_or_h_sc) {
  LV_OP_SC(int16_t, 2, |)
}
INSTRUCTION (lv_xor_b_sc) {
  LV_OP_SC(int8_t, 4, ^)
}
INSTRUCTION (lv_xor_h_sc) {
  LV_OP_SC(int16_t, 2, ^)
}
INSTRUCTION (lv_and_b_sc) {
  LV_OP_SC(int8_t, 4, &)
}
INSTRUCTION (lv_and_h_sc) {
  LV_OP_SC(int16_t, 2, &)
}

INSTRUCTION (lv_add_b_sci) {
  LV_OP_SC(int8_t, 4, +)
}
INSTRUCTION (lv_add_h_sci) {
  LV_OP_SC(int16_t, 2, +)
}
INSTRUCTION (lv_sub_b_sci) {
  LV_OP_SC(int8_t, 4, -)
}
INSTRUCTION (lv_sub_h_sci) {
  LV_OP_SC(int16_t, 2, -)
}
INSTRUCTION (lv_avg_b_sci) {
  LV_EXPR_SC(int8_t, 4, ((int8_t)(tmp_a[i] + tmp_b)>>(int8_t)1))
}
INSTRUCTION (lv_avg_h_sci) {
  LV_EXPR_SC(int16_t, 2, ((int16_t)(tmp_a[i] + tmp_b)>>(int16_t)1))
}
INSTRUCTION (lv_min_b_sci) {
  LV_EXPR_SC(int8_t, 4, (tmp_a[i] > tmp_b ? tmp_b : tmp_a[i]))
}
INSTRUCTION (lv_min_h_sci) {
  LV_EXPR_SC(int16_t, 2, (tmp_a[i] > tmp_b ? tmp_b : tmp_a[i]))
}
INSTRUCTION (lv_max_b_sci) {
  LV_EXPR_SC(int8_t, 4, (tmp_a[i] > tmp_b ? tmp_a[i] : tmp_b))
}
INSTRUCTION (lv_max_h_sci) {
  LV_EXPR_SC(int16_t, 2, (tmp_a[i] > tmp_b ? tmp_a[i] : tmp_b))
}
INSTRUCTION (lv_srl_b_sci) {
  LV_OP_SC(uint8_t, 4, >>)
}
INSTRUCTION (lv_srl_h_sci) {
  LV_OP_SC(uint16_t, 2, >>)
}
INSTRUCTION (lv_sra_b_sci) {
  LV_OP_SC(int8_t, 4, >>)
}
INSTRUCTION (lv_sra_h_sci) {
  LV_OP_SC(int16_t, 2, >>)
}
INSTRUCTION (lv_sll_b_sci) {
  LV_OP_SC(uint8_t, 4, <<)
}
INSTRUCTION (lv_sll_h_sci) {
  LV_OP_SC(uint16_t, 2, <<)
}
INSTRUCTION (lv_mul_b_sci) {
  LV_OP_SC(int8_t, 4, *)
}
INSTRUCTION (lv_mul_h_sci) {
  LV_OP_SC(int16_t, 2, *)
}
INSTRUCTION (lv_or_b_sci) {
  LV_OP_SC(int8_t, 4, |)
}
INSTRUCTION (lv_or_h_sci) {
  LV_OP_SC(int16_t, 2, |)
}
INSTRUCTION (lv_xor_b_sci) {
  LV_OP_SC(int8_t, 4, ^)
}
INSTRUCTION (lv_xor_h_sci) {
  LV_OP_SC(int16_t, 2, ^)
}
INSTRUCTION (lv_and_b_sci) {
  LV_OP_SC(int8_t, 4, &)
}
INSTRUCTION (lv_and_h_sci) {
  LV_OP_SC(int16_t, 2, &)
}

INSTRUCTION (lv_abs_b) {
  int8_t *tmp_a = (int8_t*)&PARAM1;
  int8_t tmp_out[4];
  int i;
  for (i = 0; i < 4; i++)
    tmp_out[i] = tmp_a[i] > 0 ? tmp_a[i] : -tmp_a[i];
  SET_PARAM0(*((uorreg_t*)tmp_out));
}
INSTRUCTION (lv_abs_h) {
  int16_t *tmp_a = (int16_t*)&PARAM1;
  int16_t tmp_out[2];
  int i;
  for (i = 0; i < 2; i++)
    tmp_out[i] = tmp_a[i] > 0 ? tmp_a[i] : -tmp_a[i];
  SET_PARAM0(*((uorreg_t*)tmp_out));
}


INSTRUCTION (lv_ins_b) {
  // PARAM0 -> rD
  // PARAM1 -> rA
  // PARAM2 -> rB
  // PARAM3 -> K
  uint32_t shift = PARAM3 * 8;
  uint32_t mask = 0xff << shift;
  uint32_t ins = (PARAM2 & 0xff) << shift;

  uint32_t val = PARAM1;
  val &= ~mask;
  val |= ins;

  SET_PARAM0(val);
}
INSTRUCTION (lv_ins_h) {
  // PARAM0 -> rD
  // PARAM1 -> rA
  // PARAM2 -> rB
  // PARAM3 -> K
  uint32_t shift = PARAM3 * 16;
  uint32_t mask = 0xffff << shift;
  uint32_t ins = (PARAM2 & 0xffff) << shift;

  uint32_t val = PARAM1;
  val &= ~mask;
  val |= ins;

  SET_PARAM0(val);
}
INSTRUCTION (lv_ext_b) {
  // PARAM0 -> rD
  // PARAM1 -> rA
  // PARAM2 -> L
  uint32_t shift = PARAM2 * 8;
  uint32_t mask = 0xff << shift;

  uint32_t val = PARAM1;
  val &= mask;
  val >>= shift;

  SET_PARAM0((int8_t)val);
}
INSTRUCTION (lv_ext_h) {
  // PARAM0 -> rD
  // PARAM1 -> rA
  // PARAM2 -> L
  uint32_t shift = PARAM2 * 16;
  uint32_t mask = 0xffff << shift;

  uint32_t val = PARAM1;
  val &= mask;
  val >>= shift;

  SET_PARAM0((int16_t)val);
}

/******* Custom instructions *******/
INSTRUCTION (l_cust1) {
  /*int destr = current->insn >> 21;
    int src1r = current->insn >> 15;
    int src2r = current->insn >> 9;*/
}
INSTRUCTION (l_cust2) {
}
INSTRUCTION (l_cust3) {
}
INSTRUCTION (l_cust4) {
}
INSTRUCTION (lf_cust1) {
}
