text: /*
   * RISCV emulator
   * 
   * Copyright (c) 2016 Fabrice Bellard
   *
   * MARSS-RISCV : Micro-Architectural System Simulator for RISC-V
   *
   * Copyright (c) 2017-2019 Gaurav Kothari {gkothar1@binghamton.edu}
   * State University of New York at Binghamton
   *
   * Copyright (c) 2018-2019 Parikshit Sarnaik {psarnai1@binghamton.edu}
   * State University of New York at Binghamton
   * 
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  #if XLEN == 32
  #define uintx_t uint32_t
  #define intx_t int32_t
  #elif XLEN == 64
  #define uintx_t uint64_t
  #define intx_t int64_t
  #elif XLEN == 128
  #define uintx_t uint128_t
  #define intx_t int128_t
  #else
  #error unsupported XLEN
  #endif
  
  static inline intx_t glue(div, XLEN)(intx_t a, intx_t b)
  {
      if (b == 0) {
          return -1;
      } else if (a == ((intx_t)1 << (XLEN - 1)) && b == -1) {
          return a;
      } else {
          return a / b;
      }
  }
  
  static inline uintx_t glue(divu, XLEN)(uintx_t a, uintx_t b)
  {
      if (b == 0) {
          return -1;
      } else {
          return a / b;
      }
  }
  
  static inline intx_t glue(rem, XLEN)(intx_t a, intx_t b)
  {
      if (b == 0) {
          return a;
      } else if (a == ((intx_t)1 << (XLEN - 1)) && b == -1) {
          return 0;
      } else {
          return a % b;
      }
  }
  
  static inline uintx_t glue(remu, XLEN)(uintx_t a, uintx_t b)
  {
      if (b == 0) {
          return a;
      } else {
          return a % b;
      }
  }
  
  #if XLEN == 32
  
  static inline uint32_t mulh32(int32_t a, int32_t b)
  {
      return ((int64_t)a * (int64_t)b) >> 32;
  }
  
  static inline uint32_t mulhsu32(int32_t a, uint32_t b)
  {
      return ((int64_t)a * (int64_t)b) >> 32;
  }
  
  static inline uint32_t mulhu32(uint32_t a, uint32_t b)
  {
      return ((int64_t)a * (int64_t)b) >> 32;
  }
  
  #elif XLEN == 64 && defined(HAVE_INT128)
  
  static inline uint64_t mulh64(int64_t a, int64_t b)
  {
      return ((int128_t)a * (int128_t)b) >> 64;
  }
  
  static inline uint64_t mulhsu64(int64_t a, uint64_t b)
  {
      return ((int128_t)a * (int128_t)b) >> 64;
  }
  
  static inline uint64_t mulhu64(uint64_t a, uint64_t b)
  {
      return ((int128_t)a * (int128_t)b) >> 64;
  }
  
  #else
  
  #if XLEN == 64
  #define UHALF uint32_t
  #define UHALF_LEN 32
  #elif XLEN == 128
  #define UHALF uint64_t
  #define UHALF_LEN 64
  #else
  #error unsupported XLEN
  #endif
  
  static uintx_t glue(mulhu, XLEN)(uintx_t a, uintx_t b)
  {
      UHALF a0, a1, b0, b1, r2, r3;
      uintx_t r00, r01, r10, r11, c;
      a0 = a;
      a1 = a >> UHALF_LEN;
      b0 = b;
      b1 = b >> UHALF_LEN;
  
      r00 = (uintx_t)a0 * (uintx_t)b0;
      r01 = (uintx_t)a0 * (uintx_t)b1;
      r10 = (uintx_t)a1 * (uintx_t)b0;
      r11 = (uintx_t)a1 * (uintx_t)b1;
      
      //    r0 = r00;
      c = (r00 >> UHALF_LEN) + (UHALF)r01 + (UHALF)r10;
      //    r1 = c;
      c = (c >> UHALF_LEN) + (r01 >> UHALF_LEN) + (r10 >> UHALF_LEN) + (UHALF)r11;
      r2 = c;
      r3 = (c >> UHALF_LEN) + (r11 >> UHALF_LEN);
  
      //    *plow = ((uintx_t)r1 << UHALF_LEN) | r0;
      return ((uintx_t)r3 << UHALF_LEN) | r2;
  }
  
  #undef UHALF
  
  static inline uintx_t glue(mulh, XLEN)(intx_t a, intx_t b)
  {
      uintx_t r1;
      r1 = glue(mulhu, XLEN)(a, b);
      if (a < 0)
          r1 -= a;
      if (b < 0)
          r1 -= b;
      return r1;
  }
  
  static inline uintx_t glue(mulhsu, XLEN)(intx_t a, uintx_t b)
  {
      uintx_t r1;
      r1 = glue(mulhu, XLEN)(a, b);
      if (a < 0)
          r1 -= a;
      return r1;
  }
  
  #endif
  
  #define DUP2(F, n) F(n) F(n+1)
  #define DUP4(F, n) DUP2(F, n) DUP2(F, n + 2)
  #define DUP8(F, n) DUP4(F, n) DUP4(F, n + 4)
  #define DUP16(F, n) DUP8(F, n) DUP8(F, n + 8)
  #define DUP32(F, n) DUP16(F, n) DUP16(F, n + 16)
  
  #define C_QUADRANT(n) \
      case n+(0 << 2): case n+(1 << 2): case n+(2 << 2): case n+(3 << 2): \
      case n+(4 << 2): case n+(5 << 2): case n+(6 << 2): case n+(7 << 2): \
      case n+(8 << 2): case n+(9 << 2): case n+(10 << 2): case n+(11 << 2): \
      case n+(12 << 2): case n+(13 << 2): case n+(14 << 2): case n+(15 << 2): \
      case n+(16 << 2): case n+(17 << 2): case n+(18 << 2): case n+(19 << 2): \
      case n+(20 << 2): case n+(21 << 2): case n+(22 << 2): case n+(23 << 2): \
      case n+(24 << 2): case n+(25 << 2): case n+(26 << 2): case n+(27 << 2): \
      case n+(28 << 2): case n+(29 << 2): case n+(30 << 2): case n+(31 << 2): 
  
  #define GET_PC() (target_ulong)((uintptr_t)code_ptr + code_to_pc_addend)
  #define GET_INSN_COUNTER() (insn_counter_addend - s->n_cycles)
  
  #define C_NEXT_INSN code_ptr += 2; break
  #define NEXT_INSN code_ptr += 4; break
  #define JUMP_INSN do {   \
          code_ptr = NULL;           \
          code_end = NULL;           \
          code_to_pc_addend = s->pc; \
          goto jump_insn;            \
      } while (0)
  
  static void no_inline glue(riscv_cpu_interp_x, XLEN)(RISCVCPUState *s,
                                                     int n_cycles1)
  {
      uint32_t opcode, insn, rd, rs1, rs2, funct3;
      int32_t imm, cond, err;
      int run_mode = 0;
      int sim_exit_status;
      target_ulong addr, val, val2;
  #ifndef USE_GLOBAL_VARIABLES
      uint8_t *code_ptr, *code_end;
      target_ulong code_to_pc_addend;
  #endif
      uint64_t insn_counter_addend;
  #if FLEN > 0
      uint32_t rs3;
      int32_t rm;
  #endif
  
      if (n_cycles1 == 0)
          return;
      insn_counter_addend = s->insn_counter + n_cycles1;
      s->n_cycles = n_cycles1;
  
      /* check pending interrupts */
      if (unlikely((s->mip & s->mie) != 0)) {
          if (raise_interrupt(s)) {
              s->n_cycles--; 
              goto done_interp;
          }
      }
  
      s->pending_exception = -1;
      /* Note: we assume NULL is represented as a zero number */
      code_ptr = NULL;
      code_end = NULL;
      code_to_pc_addend = s->pc;
  
      if (s->start_simulation)
      {
          s->simulation = TRUE;
          s->start_simulation = FALSE;
          s->simcpu->clock = 0;
  
          /* Reset simulation stats */
          sim_stats_reset(s->simcpu->stats);
  
          START_SIM_TIMER(s->sim_start);
  
          /* Reset BPU at every new simulation run */
          if (s->sim_params->enable_bpu)
          {
              bpu_flush(s->simcpu->bpu);
          }
  
          /* Reset Caches at every new simulation run */
          if (s->sim_params->enable_l1_caches)
          {
              reset_cache_stats(s->simcpu->mmu->icache);
              reset_cache_stats(s->simcpu->mmu->dcache);
  
              if (s->sim_params->flush_sim_mem)
              {
                  cache_flush(s->simcpu->mmu->icache);
                  cache_flush(s->simcpu->mmu->dcache);
              }
  
              if (s->sim_params->enable_l2_cache)
              {
                  reset_cache_stats(s->simcpu->mmu->l2_cache);
  
                  if (s->sim_params->flush_sim_mem)
                  {
                      cache_flush(s->simcpu->mmu->l2_cache);
                  }
              }
          }
  
          /* Reset DRAMs at every new simulation run */
          switch (s->simcpu->mmu->mem_controller->mem_model_type)
          {
              case MEM_MODEL_BASE:
              {
                  break;
              }
              case MEM_MODEL_DRAMSIM:
              {
                  dramsim_wrapper_destroy();
                  dramsim_wrapper_init(
                      s->sim_params->dramsim_ini_file,
                      s->sim_params->dramsim_system_ini_file,
                      s->sim_params->dramsim_stats_dir, s->sim_params->core_name,
                      s->sim_params->guest_ram_size,
                      &s->simcpu->mmu->mem_controller->frontend_mem_access_queue,
                      &s->simcpu->mmu->mem_controller->backend_mem_access_queue);
                  break;
              }
          }
  
          /* Open trace file if running in trace mode */
          if (s->simcpu->params->do_sim_trace)
          {
              s->sim_trace = fopen(s->sim_params->sim_trace_file, "w");
              assert(s->sim_trace);
              s->sim_params->create_ins_str = TRUE;
          }
  
          fprintf(stderr, "(marss-riscv): Switching to full-system simulation "
                          "mode at pc = 0x%" PR_target_ulong "\n",
                  s->pc);
      }
  
      if (s->simulation)
      {
          /* Prepare the simulation context */
          s->code_ptr = code_ptr;
          s->code_end = code_end;
          s->code_to_pc_addend = code_to_pc_addend;
          s->sim_n_cycles = s->n_cycles;
  
          /* Switch to simulation */
          sim_exit_status = switch_to_cpu_simulation(s);
  
          /* Exit from simulation due to an exception */
          s->n_cycles = s->sim_n_cycles;
          code_ptr = NULL;
          code_end = NULL;
  
          /* Set PC to the instruction which caused exception */
          code_to_pc_addend = s->sim_epc;
          // s->pc = s->sim_epc;
  
          /* Process exception and replay the instruction in the emulator (only
           * the complex opcodes, illegal opcodes and timer) */
          switch (sim_exit_status)
          {
              case SIM_ILLEGAL_OPCODE:
              {
                  /* Illegal opcode */
                  if (s->simcpu->params->do_sim_trace)
                  {
                      sim_print_exp_trace(s);
                  }
                  goto illegal_insn;
                  break;
              }
  
              case SIM_COMPLEX_OPCODE:
              {
                  /* Includes SYSTEM opcode instructions (eg, ecall,ebreak) which
                   * we simulate.in a single cycle  */
                  s->return_to_sim = 1;
                  ++s->simcpu->stats[s->priv].ins_simulated;
  
                  /* For counting system opcode instructions, because stats for
                   * instruction types are updated in commit stage and we don't
                   * allow system instruction to commit in pipeline, we commit
                   * them here */
                  ++s->simcpu->stats[s->priv].ins_emulated;
  
                  if (s->simcpu->params->do_sim_trace)
                  {
                      sim_print_exp_trace(s);
                  }
                  break;
              }
  
              case SIM_TIMEOUT_EXCEPTION:
              {
                  /* We executed all the n_cycles instructions for this interval,
                   * and now we must exit to virt_machine_run() to receive
                   * interrupts. */
                  assert(s->n_cycles == 0);
                  if (s->simcpu->params->do_sim_trace)
                  {
                      sim_print_exp_trace(s);
                  }
                  break;
              }
  
              case SIM_MMU_EXCEPTION:
              {
                  if (s->simcpu->params->do_sim_trace)
                  {
                      sim_print_exp_trace(s);
                  }
                  /* Directly go to mmu_exception as TLB lookup is finished on
                   * simulation side and the relevant exception cause (page fault,
                   * invalid access) has already been set */
                  goto mmu_exception;
                  break;
              }
              default:
                  fprintf(stderr, "%s\n", "Invalid exception cause");
                  exit(1);
          }
      }
  
      /* we use a single execution loop to keep a simple control flow
         for emscripten */
      for(;;) {
          if (unlikely(code_ptr >= code_end)) {
              uint32_t tlb_idx;
              uint16_t insn_high;
              target_ulong addr;
              uint8_t *ptr;
              
              s->pc = GET_PC();
              /* we test n_cycles only between blocks so that timer
                 interrupts only happen between the blocks. It is
                 important to reduce the translated code size. */
              if (unlikely(s->n_cycles <= 0))
                  goto the_end;
  
              /* check pending interrupts */
              if (unlikely((s->mip & s->mie) != 0)) {
                  if (raise_interrupt(s)) {
                      s->n_cycles--; 
                      goto the_end;
                  }
              }
      
              addr = s->pc;
              tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);
              if (likely(s->tlb_code[tlb_idx].vaddr == (addr & ~PG_MASK))) {
                  /* TLB match */ 
                  ptr = (uint8_t *)(s->tlb_code[tlb_idx].mem_addend +
                                    (uintptr_t)addr);
              } else {
                  if (unlikely(target_read_insn_slow(s, &ptr, addr)))
                      goto mmu_exception;
              }
              code_ptr = ptr;
              code_end = ptr + (PG_MASK - 1 - (addr & PG_MASK));
              code_to_pc_addend = addr - (uintptr_t)code_ptr;
              if (unlikely(code_ptr >= code_end)) {
                  /* instruction is potentially half way between two
                     pages ? */
                  insn = *(uint16_t *)code_ptr;
                  if ((insn & 3) == 3) {
                      /* instruction is half way between two pages */
                      if (unlikely(target_read_insn_u16(s, &insn_high, addr + 2)))
                          goto mmu_exception;
                      insn |= insn_high << 16;
                  }
              } else {
                  insn = get_insn32(code_ptr);
              }
          } else {
              /* fast path */
              insn = get_insn32(code_ptr);
          }
          s->n_cycles--;
  #if 0
          if (1) {
  #ifdef CONFIG_LOGFILE
              log_printf("pc=0x"); fprint_target_ulong(log_file, GET_PC()); log_printf(" insn=%08x\n", insn);
              fflush(log_file);
  #else
              printf("pc=0x"); print_target_ulong(GET_PC()); printf(" insn=%08x\n", insn);
              //            dump_regs(s);
  #endif
          }
  #endif
          opcode = insn & 0x7f;
          rd = (insn >> 7) & 0x1f;
          rs1 = (insn >> 15) & 0x1f;
          rs2 = (insn >> 20) & 0x1f;
          switch(opcode) {
  #ifdef CONFIG_EXT_C
          C_QUADRANT(0)
              funct3 = (insn >> 13) & 7;
              rd = ((insn >> 2) & 7) | 8;
              switch(funct3) {
              case 0: /* c.addi4spn */
                  imm = get_field1(insn, 11, 4, 5) |
                      get_field1(insn, 7, 6, 9) |
                      get_field1(insn, 6, 2, 2) |
                      get_field1(insn, 5, 3, 3);
                  if (imm == 0)
                      goto illegal_insn;
                  s->reg[rd] = (intx_t)(s->reg[2] + imm);
                  break;
  #if XLEN >= 128
              case 1: /* c.lq */
                  imm = get_field1(insn, 11, 4, 5) |
                      get_field1(insn, 10, 8, 8) |
                      get_field1(insn, 5, 6, 7);
                  rs1 = ((insn >> 7) & 7) | 8;
                  addr = (intx_t)(s->reg[rs1] + imm);
                  if (target_read_u128(s, &val, addr))
                      goto mmu_exception;
                  s->reg[rd] = val;
                  break;
  #elif FLEN >= 64
              case 1: /* c.fld */
                  {
                      uint64_t rval;
                      if (s->fs == 0)
                          goto illegal_insn;
                      imm = get_field1(insn, 10, 3, 5) |
                          get_field1(insn, 5, 6, 7);
                      rs1 = ((insn >> 7) & 7) | 8;
                      addr = (intx_t)(s->reg[rs1] + imm);
                      if (target_read_u64(s, &rval, addr))
                          goto mmu_exception;
                      s->fp_reg[rd] = rval | F64_HIGH;
                      s->fs = 3;
                  }
                  break;
  #endif
              case 2: /* c.lw */
                  {
                      uint32_t rval;
                      imm = get_field1(insn, 10, 3, 5) |
                          get_field1(insn, 6, 2, 2) |
                          get_field1(insn, 5, 6, 6);
                      rs1 = ((insn >> 7) & 7) | 8;
                      addr = (intx_t)(s->reg[rs1] + imm);
                      if (target_read_u32(s, &rval, addr))
                          goto mmu_exception;
                      s->reg[rd] = (int32_t)rval;
                  }
                  break;
  #if XLEN >= 64
              case 3: /* c.ld */
                  {
                      uint64_t rval;
                      imm = get_field1(insn, 10, 3, 5) |
                          get_field1(insn, 5, 6, 7);
                      rs1 = ((insn >> 7) & 7) | 8;
                      addr = (intx_t)(s->reg[rs1] + imm);
                      if (target_read_u64(s, &rval, addr))
                          goto mmu_exception;
                      s->reg[rd] = (int64_t)rval;
                  }
                  break;
  #elif FLEN >= 32
              case 3: /* c.flw */
                  {
                      uint32_t rval;
                      if (s->fs == 0)
                          goto illegal_insn;
                      imm = get_field1(insn, 10, 3, 5) |
                          get_field1(insn, 6, 2, 2) |
                          get_field1(insn, 5, 6, 6);
                      rs1 = ((insn >> 7) & 7) | 8;
                      addr = (intx_t)(s->reg[rs1] + imm);
                      if (target_read_u32(s, &rval, addr))
                          goto mmu_exception;
                      s->fp_reg[rd] = rval | F32_HIGH;
                      s->fs = 3;
                  }
                  break;
  #endif
  #if XLEN >= 128
              case 5: /* c.sq */
                  imm = get_field1(insn, 11, 4, 5) |
                      get_field1(insn, 10, 8, 8) |
                      get_field1(insn, 5, 6, 7);
                  rs1 = ((insn >> 7) & 7) | 8;
                  addr = (intx_t)(s->reg[rs1] + imm);
                  val = s->reg[rd];
                  if (target_write_u128(s, addr, val))
                      goto mmu_exception;
                  break;
  #elif FLEN >= 64
              case 5: /* c.fsd */
                  if (s->fs == 0)
                      goto illegal_insn;
                  imm = get_field1(insn, 10, 3, 5) |
                      get_field1(insn, 5, 6, 7);
                  rs1 = ((insn >> 7) & 7) | 8;
                  addr = (intx_t)(s->reg[rs1] + imm);
                  if (target_write_u64(s, addr, s->fp_reg[rd]))
                      goto mmu_exception;
                  break;
  #endif
              case 6: /* c.sw */
                  imm = get_field1(insn, 10, 3, 5) |
                      get_field1(insn, 6, 2, 2) |
                      get_field1(insn, 5, 6, 6);
                  rs1 = ((insn >> 7) & 7) | 8;
                  addr = (intx_t)(s->reg[rs1] + imm);
                  val = s->reg[rd];
                  if (target_write_u32(s, addr, val))
                      goto mmu_exception;
                  break;
  #if XLEN >= 64
              case 7: /* c.sd */
                  imm = get_field1(insn, 10, 3, 5) |
                      get_field1(insn, 5, 6, 7);
                  rs1 = ((insn >> 7) & 7) | 8;
                  addr = (intx_t)(s->reg[rs1] + imm);
                  val = s->reg[rd];
                  if (target_write_u64(s, addr, val))
                      goto mmu_exception;
                  break;
  #elif FLEN >= 32
              case 7: /* c.fsw */
                  if (s->fs == 0)
                      goto illegal_insn;
                  imm = get_field1(insn, 10, 3, 5) |
                      get_field1(insn, 6, 2, 2) |
                      get_field1(insn, 5, 6, 6);
                  rs1 = ((insn >> 7) & 7) | 8;
                  addr = (intx_t)(s->reg[rs1] + imm);
                  if (target_write_u32(s, addr, s->fp_reg[rd]))
                      goto mmu_exception;
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              C_NEXT_INSN;
          C_QUADRANT(1)
              funct3 = (insn >> 13) & 7;
              switch(funct3) {
              case 0: /* c.addi/c.nop */
                  if (rd != 0) {
                      imm = sext(get_field1(insn, 12, 5, 5) |
                                 get_field1(insn, 2, 0, 4), 6);
                      s->reg[rd] = (intx_t)(s->reg[rd] + imm);
                  }
                  break;
  #if XLEN == 32
              case 1: /* c.jal */
                  imm = sext(get_field1(insn, 12, 11, 11) | 
                             get_field1(insn, 11, 4, 4) |
                             get_field1(insn, 9, 8, 9) |
                             get_field1(insn, 8, 10, 10) |
                             get_field1(insn, 7, 6, 6) |
                             get_field1(insn, 6, 7, 7) |
                             get_field1(insn, 3, 1, 3) |
                             get_field1(insn, 2, 5, 5), 12);
                  s->reg[1] = GET_PC() + 2;
                  s->pc = (intx_t)(GET_PC() + imm);
                  JUMP_INSN;
  #else
              case 1: /* c.addiw */
                  if (rd != 0) {
                      imm = sext(get_field1(insn, 12, 5, 5) |
                                 get_field1(insn, 2, 0, 4), 6);
                      s->reg[rd] = (int32_t)(s->reg[rd] + imm);
                  }
                  break;
  #endif
              case 2: /* c.li */
                  if (rd != 0) {
                      imm = sext(get_field1(insn, 12, 5, 5) |
                                 get_field1(insn, 2, 0, 4), 6);
                      s->reg[rd] = imm;
                  }
                  break;
              case 3:
                  if (rd == 2) {
                      /* c.addi16sp */
                      imm = sext(get_field1(insn, 12, 9, 9) |
                                 get_field1(insn, 6, 4, 4) |
                                 get_field1(insn, 5, 6, 6) |
                                 get_field1(insn, 3, 7, 8) |
                                 get_field1(insn, 2, 5, 5), 10);
                      if (imm == 0)
                          goto illegal_insn;
                      s->reg[2] = (intx_t)(s->reg[2] + imm);
                  } else if (rd != 0) {
                      /* c.lui */
                      imm = sext(get_field1(insn, 12, 17, 17) |
                                 get_field1(insn, 2, 12, 16), 18);
                      s->reg[rd] = imm;
                  }
                  break;
              case 4: 
                  funct3 = (insn >> 10) & 3;
                  rd = ((insn >> 7) & 7) | 8;
                  switch(funct3) {
                  case 0: /* c.srli */ 
                  case 1: /* c.srai */ 
                      imm = get_field1(insn, 12, 5, 5) |
                          get_field1(insn, 2, 0, 4);
  #if XLEN == 32
                      if (imm & 0x20)
                          goto illegal_insn;
  #elif XLEN == 128
                      if (imm == 0)
                          imm = 64;
                      else if (imm >= 32)
                          imm = 128 - imm;
  #endif
                      if (funct3 == 0)
                          s->reg[rd] = (intx_t)((uintx_t)s->reg[rd] >> imm);
                      else
                          s->reg[rd] = (intx_t)s->reg[rd] >> imm;
                      
                      break;
                  case 2: /* c.andi */
                      imm = sext(get_field1(insn, 12, 5, 5) |
                                 get_field1(insn, 2, 0, 4), 6);
                      s->reg[rd] &= imm;
                      break;
                  case 3: 
                      rs2 = ((insn >> 2) & 7) | 8;
                      funct3 = ((insn >> 5) & 3) | ((insn >> (12 - 2)) & 4);
                      switch(funct3) {
                      case 0: /* c.sub */
                          s->reg[rd] = (intx_t)(s->reg[rd] - s->reg[rs2]);
                          break;
                      case 1: /* c.xor */
                          s->reg[rd] = s->reg[rd] ^ s->reg[rs2];
                          break;
                      case 2: /* c.or */
                          s->reg[rd] = s->reg[rd] | s->reg[rs2];
                          break;
                      case 3: /* c.and */
                          s->reg[rd] = s->reg[rd] & s->reg[rs2];
                          break;
  #if XLEN >= 64
                      case 4: /* c.subw */
                          s->reg[rd] = (int32_t)(s->reg[rd] - s->reg[rs2]);
                          break;
                      case 5: /* c.addw */
                          s->reg[rd] = (int32_t)(s->reg[rd] + s->reg[rs2]);
                          break;
  #endif
                      default:
                          goto illegal_insn;
                      }
                      break;
                  }
                  break;
              case 5: /* c.j */
                  imm = sext(get_field1(insn, 12, 11, 11) | 
                             get_field1(insn, 11, 4, 4) |
                             get_field1(insn, 9, 8, 9) |
                             get_field1(insn, 8, 10, 10) |
                             get_field1(insn, 7, 6, 6) |
                             get_field1(insn, 6, 7, 7) |
                             get_field1(insn, 3, 1, 3) |
                             get_field1(insn, 2, 5, 5), 12);
                  s->pc = (intx_t)(GET_PC() + imm);
                  JUMP_INSN;
              case 6: /* c.beqz */
                  rs1 = ((insn >> 7) & 7) | 8;
                  imm = sext(get_field1(insn, 12, 8, 8) | 
                             get_field1(insn, 10, 3, 4) |
                             get_field1(insn, 5, 6, 7) |
                             get_field1(insn, 3, 1, 2) |
                             get_field1(insn, 2, 5, 5), 9);
                  if (s->reg[rs1] == 0) {
                      s->pc = (intx_t)(GET_PC() + imm);
                      JUMP_INSN;
                  }
                  break;
              case 7: /* c.bnez */
                  rs1 = ((insn >> 7) & 7) | 8;
                  imm = sext(get_field1(insn, 12, 8, 8) | 
                             get_field1(insn, 10, 3, 4) |
                             get_field1(insn, 5, 6, 7) |
                             get_field1(insn, 3, 1, 2) |
                             get_field1(insn, 2, 5, 5), 9);
                  if (s->reg[rs1] != 0) {
                      s->pc = (intx_t)(GET_PC() + imm);
                      JUMP_INSN;
                  }
                  break;
              default:
                  goto illegal_insn;
              }
              C_NEXT_INSN;
          C_QUADRANT(2)
              funct3 = (insn >> 13) & 7;
              rs2 = (insn >> 2) & 0x1f;
              switch(funct3) {
              case 0: /* c.slli */
                  imm = get_field1(insn, 12, 5, 5) | rs2;
  #if XLEN == 32
                  if (imm & 0x20)
                      goto illegal_insn;
  #elif XLEN == 128
                  if (imm == 0)
                      imm = 64;
  #endif
                  if (rd != 0)
                      s->reg[rd] = (intx_t)(s->reg[rd] << imm);
                  break;
  #if XLEN == 128
              case 1: /* c.lqsp */
                  imm = get_field1(insn, 12, 5, 5) |
                      (rs2 & (1 << 4)) |
                      get_field1(insn, 2, 6, 9);
                  addr = (intx_t)(s->reg[2] + imm);
                  if (target_read_u128(s, &val, addr))
                      goto mmu_exception;
                  if (rd != 0)
                      s->reg[rd] = val;
                  break;
  #elif FLEN >= 64
              case 1: /* c.fldsp */
                  {
                      uint64_t rval;
                      if (s->fs == 0)
                          goto illegal_insn;
                      imm = get_field1(insn, 12, 5, 5) |
                          (rs2 & (3 << 3)) |
                          get_field1(insn, 2, 6, 8);
                      addr = (intx_t)(s->reg[2] + imm);
                      if (target_read_u64(s, &rval, addr))
                          goto mmu_exception;
                      s->fp_reg[rd] = rval | F64_HIGH;
                      s->fs = 3;
                  }
                  break;
  #endif
              case 2: /* c.lwsp */
                  {
                      uint32_t rval;
                      imm = get_field1(insn, 12, 5, 5) |
                          (rs2 & (7 << 2)) |
                          get_field1(insn, 2, 6, 7);
                      addr = (intx_t)(s->reg[2] + imm);
                      if (target_read_u32(s, &rval, addr))
                          goto mmu_exception;
                      if (rd != 0)
                          s->reg[rd] = (int32_t)rval;
                  }
                  break;
  #if XLEN >= 64
              case 3: /* c.ldsp */
                  {
                      uint64_t rval;
                      imm = get_field1(insn, 12, 5, 5) |
                          (rs2 & (3 << 3)) |
                          get_field1(insn, 2, 6, 8);
                      addr = (intx_t)(s->reg[2] + imm);
                      if (target_read_u64(s, &rval, addr))
                          goto mmu_exception;
                      if (rd != 0)
                          s->reg[rd] = (int64_t)rval;
                  }
                  break;
  #elif FLEN >= 32
              case 3: /* c.flwsp */
                  {
                      uint32_t rval;
                      if (s->fs == 0)
                          goto illegal_insn;
                      imm = get_field1(insn, 12, 5, 5) |
                          (rs2 & (7 << 2)) |
                          get_field1(insn, 2, 6, 7);
                      addr = (intx_t)(s->reg[2] + imm);
                      if (target_read_u32(s, &rval, addr))
                          goto mmu_exception;
                      s->fp_reg[rd] = rval | F32_HIGH;
                      s->fs = 3;
                  }
                  break;
  #endif
              case 4:
                  if (((insn >> 12) & 1) == 0) {
                      if (rs2 == 0) {
                          /* c.jr */
                          if (rd == 0)
                              goto illegal_insn;
                          s->pc = s->reg[rd] & ~1;
                          JUMP_INSN;
                      } else {
                          /* c.mv */
                          if (rd != 0)
                              s->reg[rd] = s->reg[rs2];
                      }
                  } else {
                      if (rs2 == 0) {
                          if (rd == 0) {
                              /* c.ebreak */
                              s->pending_exception = CAUSE_BREAKPOINT;
                              goto exception;
                          } else {
                              /* c.jalr */
                              val = GET_PC() + 2;
                              s->pc = s->reg[rd] & ~1;
                              s->reg[1] = val;
                              JUMP_INSN;
                          }
                      } else {
                          if (rd != 0) {
                              s->reg[rd] = (intx_t)(s->reg[rd] + s->reg[rs2]);
                          }
                      }
                  }
                  break;
  #if XLEN == 128
              case 5: /* c.sqsp */
                  imm = get_field1(insn, 10, 3, 5) |
                      get_field1(insn, 7, 6, 8);
                  addr = (intx_t)(s->reg[2] + imm);
                  if (target_write_u128(s, addr, s->reg[rs2]))
                      goto mmu_exception;
                  break;
  #elif FLEN >= 64
              case 5: /* c.fsdsp */
                  if (s->fs == 0)
                      goto illegal_insn;
                  imm = get_field1(insn, 10, 3, 5) |
                      get_field1(insn, 7, 6, 8);
                  addr = (intx_t)(s->reg[2] + imm);
                  if (target_write_u64(s, addr, s->fp_reg[rs2]))
                      goto mmu_exception;
                  break;
  #endif 
              case 6: /* c.swsp */
                  imm = get_field1(insn, 9, 2, 5) |
                      get_field1(insn, 7, 6, 7);
                  addr = (intx_t)(s->reg[2] + imm);
                  if (target_write_u32(s, addr, s->reg[rs2]))
                      goto mmu_exception;
                  break;
  #if XLEN >= 64
              case 7: /* c.sdsp */
                  imm = get_field1(insn, 10, 3, 5) |
                      get_field1(insn, 7, 6, 8);
                  addr = (intx_t)(s->reg[2] + imm);
                  if (target_write_u64(s, addr, s->reg[rs2]))
                      goto mmu_exception;
                  break;
  #elif FLEN >= 32
              case 7: /* c.swsp */
                  if (s->fs == 0)
                      goto illegal_insn;
                  imm = get_field1(insn, 9, 2, 5) |
                      get_field1(insn, 7, 6, 7);
                  addr = (intx_t)(s->reg[2] + imm);
                  if (target_write_u32(s, addr, s->fp_reg[rs2]))
                      goto mmu_exception;
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              C_NEXT_INSN;
  #endif /* CONFIG_EXT_C */
  
          case 0x37: /* lui */
              if (rd != 0)
                  s->reg[rd] = (int32_t)(insn & 0xfffff000);
              NEXT_INSN;
          case 0x17: /* auipc */
              if (rd != 0)
                  s->reg[rd] = (intx_t)(GET_PC() + (int32_t)(insn & 0xfffff000));
              NEXT_INSN;
          case 0x6f: /* jal */
              imm = ((insn >> (31 - 20)) & (1 << 20)) |
                  ((insn >> (21 - 1)) & 0x7fe) |
                  ((insn >> (20 - 11)) & (1 << 11)) |
                  (insn & 0xff000);
              imm = (imm << 11) >> 11;
              if (rd != 0)
                  s->reg[rd] = GET_PC() + 4;
              s->pc = (intx_t)(GET_PC() + imm);
              JUMP_INSN;
          case 0x67: /* jalr */
              imm = (int32_t)insn >> 20;
              val = GET_PC() + 4;
              s->pc = (intx_t)(s->reg[rs1] + imm) & ~1;
              if (rd != 0)
                  s->reg[rd] = val;
              JUMP_INSN;
          case 0x63:
              funct3 = (insn >> 12) & 7;
              switch(funct3 >> 1) {
              case 0: /* beq/bne */
                  cond = (s->reg[rs1] == s->reg[rs2]);
                  break;
              case 2: /* blt/bge */
                  cond = ((target_long)s->reg[rs1] < (target_long)s->reg[rs2]);
                  break;
              case 3: /* bltu/bgeu */
                  cond = (s->reg[rs1] < s->reg[rs2]);
                  break;
              default:
                  goto illegal_insn;
              }
              cond ^= (funct3 & 1);
              if (cond) {
                  imm = ((insn >> (31 - 12)) & (1 << 12)) |
                      ((insn >> (25 - 5)) & 0x7e0) |
                      ((insn >> (8 - 1)) & 0x1e) |
                      ((insn << (11 - 7)) & (1 << 11));
                  imm = (imm << 19) >> 19;
                  s->pc = (intx_t)(GET_PC() + imm);
                  JUMP_INSN;
              }
              NEXT_INSN;
          case 0x03: /* load */
              funct3 = (insn >> 12) & 7;
              imm = (int32_t)insn >> 20;
              addr = s->reg[rs1] + imm;
              switch(funct3) {
              case 0: /* lb */
                  {
                      uint8_t rval;
                      if (target_read_u8(s, &rval, addr))
                          goto mmu_exception;
                      val = (int8_t)rval;
                  }
                  break;
              case 1: /* lh */
                  {
                      uint16_t rval;
                      if (target_read_u16(s, &rval, addr))
                          goto mmu_exception;
                      val = (int16_t)rval;
                  }
                  break;
              case 2: /* lw */
                  {
                      uint32_t rval;
                      if (target_read_u32(s, &rval, addr))
                          goto mmu_exception;
                      val = (int32_t)rval;
                  }
                  break;
              case 4: /* lbu */
                  {
                      uint8_t rval;
                      if (target_read_u8(s, &rval, addr))
                          goto mmu_exception;
                      val = rval;
                  }
                  break;
              case 5: /* lhu */
                  {
                      uint16_t rval;
                      if (target_read_u16(s, &rval, addr))
                          goto mmu_exception;
                      val = rval;
                  }
                  break;
  #if XLEN >= 64
              case 3: /* ld */
                  {
                      uint64_t rval;
                      if (target_read_u64(s, &rval, addr))
                          goto mmu_exception;
                      val = (int64_t)rval;
                  }
                  break;
              case 6: /* lwu */
                  {
                      uint32_t rval;
                      if (target_read_u32(s, &rval, addr))
                          goto mmu_exception;
                      val = rval;
                  }
                  break;
  #endif
  #if XLEN >= 128
              case 7: /* ldu */
                  {
                      uint64_t rval;
                      if (target_read_u64(s, &rval, addr))
                          goto mmu_exception;
                      val = rval;
                  }
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              if (rd != 0)
                  s->reg[rd] = val;
              NEXT_INSN;
          case 0x23: /* store */
              funct3 = (insn >> 12) & 7;
              imm = rd | ((insn >> (25 - 5)) & 0xfe0);
              imm = (imm << 20) >> 20;
              addr = s->reg[rs1] + imm;
              val = s->reg[rs2];
              switch(funct3) {
              case 0: /* sb */
                  if (target_write_u8(s, addr, val))
                      goto mmu_exception;
                  break;
              case 1: /* sh */
                  if (target_write_u16(s, addr, val))
                      goto mmu_exception;
                  break;
              case 2: /* sw */
                  if (target_write_u32(s, addr, val))
                      goto mmu_exception;
                  break;
  #if XLEN >= 64
              case 3: /* sd */
                  if (target_write_u64(s, addr, val))
                      goto mmu_exception;
                  break;
  #endif
  #if XLEN >= 128
              case 4: /* sq */
                  if (target_write_u128(s, addr, val))
                      goto mmu_exception;
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              NEXT_INSN;
          case 0x13:
              funct3 = (insn >> 12) & 7;
              imm = (int32_t)insn >> 20;
              switch(funct3) {
              case 0: /* addi */
                  val = (intx_t)(s->reg[rs1] + imm);
                  break;
              case 1: /* slli */
                  if ((imm & ~(XLEN - 1)) != 0)
                      goto illegal_insn;
                  val = (intx_t)(s->reg[rs1] << (imm & (XLEN - 1)));
                  break;
              case 2: /* slti */
                  val = (target_long)s->reg[rs1] < (target_long)imm;
                  break;
              case 3: /* sltiu */
                  val = s->reg[rs1] < (target_ulong)imm;
                  break;
              case 4: /* xori */
                  val = s->reg[rs1] ^ imm;
                  break;
              case 5: /* srli/srai */
                  if ((imm & ~((XLEN - 1) | 0x400)) != 0)
                      goto illegal_insn;
                  if (imm & 0x400)
                      val = (intx_t)s->reg[rs1] >> (imm & (XLEN - 1));
                  else
                      val = (intx_t)((uintx_t)s->reg[rs1] >> (imm & (XLEN - 1)));
                  break;
              case 6: /* ori */
                  val = s->reg[rs1] | imm;
                  break;
              default:
              case 7: /* andi */
                  val = s->reg[rs1] & imm;
                  break;
              }
              if (rd != 0)
                  s->reg[rd] = val;
              NEXT_INSN;
  #if XLEN >= 64
          case 0x1b:/* OP-IMM-32 */
              funct3 = (insn >> 12) & 7;
              imm = (int32_t)insn >> 20;
              val = s->reg[rs1];
              switch(funct3) {
              case 0: /* addiw */
                  val = (int32_t)(val + imm);
                  break;
              case 1: /* slliw */
                  if ((imm & ~31) != 0)
                      goto illegal_insn;
                  val = (int32_t)(val << (imm & 31));
                  break;
              case 5: /* srliw/sraiw */
                  if ((imm & ~(31 | 0x400)) != 0)
                      goto illegal_insn;
                  if (imm & 0x400)
                      val = (int32_t)val >> (imm & 31);
                  else
                      val = (int32_t)((uint32_t)val >> (imm & 31));
                  break;
              default:
                  goto illegal_insn;
              }
              if (rd != 0)
                  s->reg[rd] = val;
              NEXT_INSN;
  #endif
  #if XLEN >= 128
          case 0x5b: /* OP-IMM-64 */
              funct3 = (insn >> 12) & 7;
              imm = (int32_t)insn >> 20;
              val = s->reg[rs1];
              switch(funct3) {
              case 0: /* addid */
                  val = (int64_t)(val + imm);
                  break;
              case 1: /* sllid */
                  if ((imm & ~63) != 0)
                      goto illegal_insn;
                  val = (int64_t)(val << (imm & 63));
                  break;
              case 5: /* srlid/sraid */
                  if ((imm & ~(63 | 0x400)) != 0)
                      goto illegal_insn;
                  if (imm & 0x400)
                      val = (int64_t)val >> (imm & 63);
                  else
                      val = (int64_t)((uint64_t)val >> (imm & 63));
                  break;
              default:
                  goto illegal_insn;
              }
              if (rd != 0)
                  s->reg[rd] = val;
              NEXT_INSN;
  #endif
          case 0x33:
              imm = insn >> 25;
              val = s->reg[rs1];
              val2 = s->reg[rs2];
              if (imm == 1) {
                  funct3 = (insn >> 12) & 7;
                  switch(funct3) {
                  case 0: /* mul */
                      val = (intx_t)((intx_t)val * (intx_t)val2);
                      break;
                  case 1: /* mulh */
                      val = (intx_t)glue(mulh, XLEN)(val, val2);
                      break;
                  case 2:/* mulhsu */
                      val = (intx_t)glue(mulhsu, XLEN)(val, val2);
                      break;
                  case 3:/* mulhu */
                      val = (intx_t)glue(mulhu, XLEN)(val, val2);
                      break;
                  case 4:/* div */
                      val = glue(div, XLEN)(val, val2);
                      break;
                  case 5:/* divu */
                      val = (intx_t)glue(divu, XLEN)(val, val2);
                      break;
                  case 6:/* rem */
                      val = glue(rem, XLEN)(val, val2);
                      break;
                  case 7:/* remu */
                      val = (intx_t)glue(remu, XLEN)(val, val2);
                      break;
                  default:
                      goto illegal_insn;
                  }
              } else {
                  if (imm & ~0x20)
                      goto illegal_insn;
                  funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3));
                  switch(funct3) {
                  case 0: /* add */
                      val = (intx_t)(val + val2);
                      break;
                  case 0 | 8: /* sub */
                      val = (intx_t)(val - val2);
                      break;
                  case 1: /* sll */
                      val = (intx_t)(val << (val2 & (XLEN - 1)));
                      break;
                  case 2: /* slt */
                      val = (target_long)val < (target_long)val2;
                      break;
                  case 3: /* sltu */
                      val = val < val2;
                      break;
                  case 4: /* xor */
                      val = val ^ val2;
                      break;
                  case 5: /* srl */
                      val = (intx_t)((uintx_t)val >> (val2 & (XLEN - 1)));
                      break;
                  case 5 | 8: /* sra */
                      val = (intx_t)val >> (val2 & (XLEN - 1));
                      break;
                  case 6: /* or */
                      val = val | val2;
                      break;
                  case 7: /* and */
                      val = val & val2;
                      break;
                  default:
                      goto illegal_insn;
                  }
              }
              if (rd != 0)
                  s->reg[rd] = val;
              NEXT_INSN;
  #if XLEN >= 64
          case 0x3b: /* OP-32 */
              imm = insn >> 25;
              val = s->reg[rs1];
              val2 = s->reg[rs2];
              if (imm == 1) {
                  funct3 = (insn >> 12) & 7;
                  switch(funct3) {
                  case 0: /* mulw */
                      val = (int32_t)((int32_t)val * (int32_t)val2);
                      break;
                  case 4:/* divw */
                      val = div32(val, val2);
                      break;
                  case 5:/* divuw */
                      val = (int32_t)divu32(val, val2);
                      break;
                  case 6:/* remw */
                      val = rem32(val, val2);
                      break;
                  case 7:/* remuw */
                      val = (int32_t)remu32(val, val2);
                      break;
                  default:
                      goto illegal_insn;
                  }
              } else {
                  if (imm & ~0x20)
                      goto illegal_insn;
                  funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3));
                  switch(funct3) {
                  case 0: /* addw */
                      val = (int32_t)(val + val2);
                      break;
                  case 0 | 8: /* subw */
                      val = (int32_t)(val - val2);
                      break;
                  case 1: /* sllw */
                      val = (int32_t)((uint32_t)val << (val2 & 31));
                      break;
                  case 5: /* srlw */
                      val = (int32_t)((uint32_t)val >> (val2 & 31));
                      break;
                  case 5 | 8: /* sraw */
                      val = (int32_t)val >> (val2 & 31);
                      break;
                  default:
                      goto illegal_insn;
                  }
              }
              if (rd != 0)
                  s->reg[rd] = val;
              NEXT_INSN;
  #endif
  #if XLEN >= 128
          case 0x7b: /* OP-64 */
              imm = insn >> 25;
              val = s->reg[rs1];
              val2 = s->reg[rs2];
              if (imm == 1) {
                  funct3 = (insn >> 12) & 7;
                  switch(funct3) {
                  case 0: /* muld */
                      val = (int64_t)((int64_t)val * (int64_t)val2);
                      break;
                  case 4:/* divd */
                      val = div64(val, val2);
                      break;
                  case 5:/* divud */
                      val = (int64_t)divu64(val, val2);
                      break;
                  case 6:/* remd */
                      val = rem64(val, val2);
                      break;
                  case 7:/* remud */
                      val = (int64_t)remu64(val, val2);
                      break;
                  default:
                      goto illegal_insn;
                  }
              } else {
                  if (imm & ~0x20)
                      goto illegal_insn;
                  funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3));
                  switch(funct3) {
                  case 0: /* addd */
                      val = (int64_t)(val + val2);
                      break;
                  case 0 | 8: /* subd */
                      val = (int64_t)(val - val2);
                      break;
                  case 1: /* slld */
                      val = (int64_t)((uint64_t)val << (val2 & 63));
                      break;
                  case 5: /* srld */
                      val = (int64_t)((uint64_t)val >> (val2 & 63));
                      break;
                  case 5 | 8: /* srad */
                      val = (int64_t)val >> (val2 & 63);
                      break;
                  default:
                      goto illegal_insn;
                  }
              }
              if (rd != 0)
                  s->reg[rd] = val;
              NEXT_INSN;
  #endif
          case 0x73:
              funct3 = (insn >> 12) & 7;
              imm = insn >> 20;
              if (funct3 & 4)
                  val = rs1;
              else
                  val = s->reg[rs1];
              funct3 &= 3;
              switch(funct3) {
              case 1: /* csrrw */
                  s->insn_counter = GET_INSN_COUNTER();
                  if (csr_read(s, &val2, imm, TRUE, &run_mode))
                      goto illegal_insn;
                  val2 = (intx_t)val2;
                  err = csr_write(s, imm, val);
                  if (err < 0)
                      goto illegal_insn;
                  if (rd != 0)
                      s->reg[rd] = val2;
                  if (err > 0) {
                      s->pc = GET_PC() + 4;
                      if (err == 2)
                          JUMP_INSN;
                      else
                          goto done_interp;
                  }
                  break;
              case 2: /* csrrs */
              case 3: /* csrrc */
                  s->insn_counter = GET_INSN_COUNTER();
                  if (csr_read(s, &val2, imm, (rs1 != 0), &run_mode))
                      goto illegal_insn;
                  val2 = (intx_t)val2;
                  switch (run_mode) {
                      case MODE_SIM_START: {
                          start_system_simulation(s, GET_PC() + 4,
                                              GET_INSN_COUNTER());
                          s->pc = GET_PC() + 4;
                          goto the_end;
                      }
                      case MODE_SIM_STOP: {
                          stop_system_simulation(s, GET_PC(), GET_INSN_COUNTER());
                          s->pc = GET_PC() + 4;
                          goto the_end;
                      }
                      default:
                          break;
                  }
                  if (rs1 != 0) {
                      if (funct3 == 2)
                          val = val2 | val;
                      else
                          val = val2 & ~val;
                      err = csr_write(s, imm, val);
                      if (err < 0)
                          goto illegal_insn;
                  } else {
                      err = 0;
                  }
                  if (rd != 0)
                      s->reg[rd] = val2;
                  if (err > 0) {
                      s->pc = GET_PC() + 4;
                      if (err == 2)
                          JUMP_INSN;
                      else
                          goto done_interp;
                  }
                  break;
              case 0:
                  switch(imm) {
                  case 0x000: /* ecall */
                      if (insn & 0x000fff80)
                          goto illegal_insn;
                      if (s->simulation)
                      {
                          ++s->simcpu->stats[s->priv].ecall;
                      }
                      s->pending_exception = CAUSE_USER_ECALL + s->priv;
                      goto exception;
                  case 0x001: /* ebreak */
                      if (insn & 0x000fff80)
                          goto illegal_insn;
                      s->pending_exception = CAUSE_BREAKPOINT;
                      goto exception;
                  case 0x102: /* sret */
                      {
                          if (insn & 0x000fff80)
                              goto illegal_insn;
                          if (s->priv < PRV_S)
                              goto illegal_insn;
                          s->pc = GET_PC();
                          handle_sret(s);
                          goto done_interp;
                      }
                      break;
                  case 0x302: /* mret */
                      {
                          if (insn & 0x000fff80)
                              goto illegal_insn;
                          if (s->priv < PRV_M)
                              goto illegal_insn;
                          s->pc = GET_PC();
                          handle_mret(s);
                          goto done_interp;
                      }
                      break;
                  case 0x105: /* wfi */
                      if (insn & 0x00007f80)
                          goto illegal_insn;
                      if (s->priv == PRV_U)
                          goto illegal_insn;
                      /* go to power down if no enabled interrupts are
                         pending */
                      if ((s->mip & s->mie) == 0) {
                          s->power_down_flag = TRUE;
                          s->pc = GET_PC() + 4;
                          goto done_interp;
                      }
                      break;
                  default:
                      if ((imm >> 5) == 0x09) {
                          /* sfence.vma */
                          if (insn & 0x00007f80)
                              goto illegal_insn;
                          if (s->priv == PRV_U)
                              goto illegal_insn;
                          if (rs1 == 0) {
                              tlb_flush_all(s);
                          } else {
                              tlb_flush_vaddr(s, s->reg[rs1]);
                          }
                          /* the current code TLB may have been flushed */
                          s->pc = GET_PC() + 4;
                          JUMP_INSN;
                      } else {
                          goto illegal_insn;
                      }
                      break;
                  }
                  break;
              default:
                  goto illegal_insn;
              }
              NEXT_INSN;
          case 0x0f: /* misc-mem */
              funct3 = (insn >> 12) & 7;
              switch(funct3) {
              case 0: /* fence */
                  if (insn & 0xf00fff80)
                      goto illegal_insn;
                  break;
              case 1: /* fence.i */
                  if (insn != 0x0000100f)
                      goto illegal_insn;
                  break;
  #if XLEN >= 128
              case 2: /* lq */
                  imm = (int32_t)insn >> 20;
                  addr = s->reg[rs1] + imm;
                  if (target_read_u128(s, &val, addr))
                      goto mmu_exception;
                  if (rd != 0)
                      s->reg[rd] = val;
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              NEXT_INSN;
          case 0x2f:
              funct3 = (insn >> 12) & 7;
  #define OP_A(size)                                                      \
              {                                                           \
                  uint ## size ##_t rval;                                 \
                                                                          \
                  addr = s->reg[rs1];                                     \
                  funct3 = insn >> 27;                                    \
                  switch(funct3) {                                        \
                  case 2: /* lr.w */                                      \
                      if (rs2 != 0)                                       \
                          goto illegal_insn;                              \
                      if (target_read_u ## size(s, &rval, addr))          \
                          goto mmu_exception;                             \
                      val = (int## size ## _t)rval;                       \
                      s->load_res = addr;                                 \
                      break;                                              \
                  case 3: /* sc.w */                                      \
                      if (s->load_res == addr) {                          \
                          if (target_write_u ## size(s, addr, s->reg[rs2])) \
                              goto mmu_exception;                         \
                          val = 0;                                        \
                      } else {                                            \
                          val = 1;                                        \
                      }                                                   \
                      break;                                              \
                  case 1: /* amiswap.w */                                 \
                  case 0: /* amoadd.w */                                  \
                  case 4: /* amoxor.w */                                  \
                  case 0xc: /* amoand.w */                                \
                  case 0x8: /* amoor.w */                                 \
                  case 0x10: /* amomin.w */                               \
                  case 0x14: /* amomax.w */                               \
                  case 0x18: /* amominu.w */                              \
                  case 0x1c: /* amomaxu.w */                              \
                      if (target_read_u ## size(s, &rval, addr))          \
                          goto mmu_exception;                             \
                      val = (int## size ## _t)rval;                       \
                      val2 = s->reg[rs2];                                 \
                      switch(funct3) {                                    \
                      case 1: /* amiswap.w */                             \
                          break;                                          \
                      case 0: /* amoadd.w */                              \
                          val2 = (int## size ## _t)(val + val2);          \
                          break;                                          \
                      case 4: /* amoxor.w */                              \
                          val2 = (int## size ## _t)(val ^ val2);          \
                          break;                                          \
                      case 0xc: /* amoand.w */                            \
                          val2 = (int## size ## _t)(val & val2);          \
                          break;                                          \
                      case 0x8: /* amoor.w */                             \
                          val2 = (int## size ## _t)(val | val2);          \
                          break;                                          \
                      case 0x10: /* amomin.w */                           \
                          if ((int## size ## _t)val < (int## size ## _t)val2) \
                              val2 = (int## size ## _t)val;               \
                          break;                                          \
                      case 0x14: /* amomax.w */                           \
                          if ((int## size ## _t)val > (int## size ## _t)val2) \
                              val2 = (int## size ## _t)val;               \
                          break;                                          \
                      case 0x18: /* amominu.w */                          \
                          if ((uint## size ## _t)val < (uint## size ## _t)val2) \
                              val2 = (int## size ## _t)val;               \
                          break;                                          \
                      case 0x1c: /* amomaxu.w */                          \
                          if ((uint## size ## _t)val > (uint## size ## _t)val2) \
                              val2 = (int## size ## _t)val;               \
                          break;                                          \
                      default:                                            \
                          goto illegal_insn;                              \
                      }                                                   \
                      if (target_write_u ## size(s, addr, val2))          \
                          goto mmu_exception;                             \
                      break;                                              \
                  default:                                                \
                      goto illegal_insn;                                  \
                  }                                                       \
              }
  
              switch(funct3) {
              case 2:
                  OP_A(32);
                  break;
  #if XLEN >= 64
              case 3:
                  OP_A(64);
                  break;
  #endif
  #if XLEN >= 128
              case 4:
                  OP_A(128);
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              if (rd != 0)
                  s->reg[rd] = val;
              NEXT_INSN;
  #if FLEN > 0
              /* FPU */
          case 0x07: /* fp load */
              if (s->fs == 0)
                  goto illegal_insn;
              funct3 = (insn >> 12) & 7;
              imm = (int32_t)insn >> 20;
              addr = s->reg[rs1] + imm;
              switch(funct3) {
              case 2: /* flw */
                  {
                      uint32_t rval;
                      if (target_read_u32(s, &rval, addr))
                          goto mmu_exception;
                      s->fp_reg[rd] = rval | F32_HIGH;
                  }
                  break;
  #if FLEN >= 64
              case 3: /* fld */
                  {
                      uint64_t rval;
                      if (target_read_u64(s, &rval, addr))
                          goto mmu_exception;
                      s->fp_reg[rd] = rval | F64_HIGH;
                  }
                  break;
  #endif 
  #if FLEN >= 128
              case 4: /* flq */
                  {
                      uint128_t rval;
                      if (target_read_u128(s, &rval, addr))
                          goto mmu_exception;
                      s->fp_reg[rd] = rval;
                  }
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              s->fs = 3;
              NEXT_INSN;
          case 0x27: /* fp store */
              if (s->fs == 0)
                  goto illegal_insn;
              funct3 = (insn >> 12) & 7;
              imm = rd | ((insn >> (25 - 5)) & 0xfe0);
              imm = (imm << 20) >> 20;
              addr = s->reg[rs1] + imm;
              switch(funct3) {
              case 2: /* fsw */
                  if (target_write_u32(s, addr, s->fp_reg[rs2]))
                      goto mmu_exception;
                  break;
  #if FLEN >= 64
              case 3: /* fsd */
                  if (target_write_u64(s, addr, s->fp_reg[rs2]))
                      goto mmu_exception;
                  break;
  #endif
  #if FLEN >= 128
              case 4: /* fsq */
                  if (target_write_u128(s, addr, s->fp_reg[rs2]))
                      goto mmu_exception;
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              NEXT_INSN;
          case 0x43: /* fmadd */
              if (s->fs == 0)
                  goto illegal_insn;
              funct3 = (insn >> 25) & 3;
              rs3 = insn >> 27;
              rm = get_insn_rm(s, (insn >> 12) & 7);
              if (rm < 0)
                  goto illegal_insn;
              switch(funct3) {
              case 0:
                  s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1], s->fp_reg[rs2],
                                           s->fp_reg[rs3], rm, &s->fflags) | F32_HIGH;
                  break;
  #if FLEN >= 64
              case 1:
                  s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1], s->fp_reg[rs2],
                                           s->fp_reg[rs3], rm, &s->fflags) | F64_HIGH;
                  break;
  #endif
  #if FLEN >= 128
              case 3:
                  s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1], s->fp_reg[rs2],
                                            s->fp_reg[rs3], rm, &s->fflags);
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              s->fs = 3;
              NEXT_INSN;
          case 0x47: /* fmsub */
              if (s->fs == 0)
                  goto illegal_insn;
              funct3 = (insn >> 25) & 3;
              rs3 = insn >> 27;
              rm = get_insn_rm(s, (insn >> 12) & 7);
              if (rm < 0)
                  goto illegal_insn;
              switch(funct3) {
              case 0:
                  s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1],
                                           s->fp_reg[rs2],
                                           s->fp_reg[rs3] ^ FSIGN_MASK32,
                                           rm, &s->fflags) | F32_HIGH;
                  break;
  #if FLEN >= 64
              case 1:
                  s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1],
                                           s->fp_reg[rs2],
                                           s->fp_reg[rs3] ^ FSIGN_MASK64,
                                           rm, &s->fflags) | F64_HIGH;
                  break;
  #endif
  #if FLEN >= 128
              case 3:
                  s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1],
                                            s->fp_reg[rs2],
                                            s->fp_reg[rs3] ^ FSIGN_MASK128,
                                            rm, &s->fflags);
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              s->fs = 3;
              NEXT_INSN;
          case 0x4b: /* fnmsub */
              if (s->fs == 0)
                  goto illegal_insn;
              funct3 = (insn >> 25) & 3;
              rs3 = insn >> 27;
              rm = get_insn_rm(s, (insn >> 12) & 7);
              if (rm < 0)
                  goto illegal_insn;
              switch(funct3) {
              case 0:
                  s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1] ^ FSIGN_MASK32,
                                           s->fp_reg[rs2],
                                           s->fp_reg[rs3],
                                           rm, &s->fflags) | F32_HIGH;
                  break;
  #if FLEN >= 64
              case 1:
                  s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1] ^ FSIGN_MASK64,
                                           s->fp_reg[rs2],
                                           s->fp_reg[rs3],
                                           rm, &s->fflags) | F64_HIGH;
                  break;
  #endif
  #if FLEN >= 128
              case 3:
                  s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1] ^ FSIGN_MASK128,
                                            s->fp_reg[rs2],
                                            s->fp_reg[rs3],
                                            rm, &s->fflags);
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              s->fs = 3;
              NEXT_INSN;
          case 0x4f: /* fnmadd */
              if (s->fs == 0)
                  goto illegal_insn;
              funct3 = (insn >> 25) & 3;
              rs3 = insn >> 27;
              rm = get_insn_rm(s, (insn >> 12) & 7);
              if (rm < 0)
                  goto illegal_insn;
              switch(funct3) {
              case 0:
                  s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1] ^ FSIGN_MASK32,
                                           s->fp_reg[rs2],
                                           s->fp_reg[rs3] ^ FSIGN_MASK32,
                                           rm, &s->fflags) | F32_HIGH;
                  break;
  #if FLEN >= 64
              case 1:
                  s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1] ^ FSIGN_MASK64,
                                           s->fp_reg[rs2],
                                           s->fp_reg[rs3] ^ FSIGN_MASK64,
                                           rm, &s->fflags) | F64_HIGH;
                  break;
  #endif
  #if FLEN >= 128
              case 3:
                  s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1] ^ FSIGN_MASK128,
                                            s->fp_reg[rs2],
                                            s->fp_reg[rs3] ^ FSIGN_MASK128,
                                            rm, &s->fflags);
                  break;
  #endif
              default:
                  goto illegal_insn;
              }
              s->fs = 3;
              NEXT_INSN;
          case 0x53:
              if (s->fs == 0)
                  goto illegal_insn;
              imm = insn >> 25;
              rm = (insn >> 12) & 7;
              switch(imm) {
  
  #define F_SIZE 32
  #include "riscv_cpu_fp_template.h"
  #if FLEN >= 64
  #define F_SIZE 64
  #include "riscv_cpu_fp_template.h"
  #endif
  #if FLEN >= 128
  #define F_SIZE 128
  #include "riscv_cpu_fp_template.h"
  #endif
  
              default:
                  goto illegal_insn;
              }
              NEXT_INSN;
  #endif
          default:
              goto illegal_insn;
          }
          /* update PC for next instruction */
      jump_insn: ;
  
          /* Return to simulation as complex opcode emulation is complete */
          if (s->return_to_sim)
          {
              s->return_to_sim = 0;
              s->pc = GET_PC();
              goto the_end;
          }
  
          /* Control should never reach here during simulation */
          if (s->simulation)
          {
              assert(0);
          }
  
      } /* end of main loop */
   illegal_insn:
      s->pending_exception = CAUSE_ILLEGAL_INSTRUCTION;
      s->pending_tval = insn;
   mmu_exception:
   exception:
      s->pc = GET_PC();
      if (s->pending_exception >= 0) {
          /* Note: the idea is that one exception counts for one cycle. */
          s->n_cycles--; 
          raise_exception2(s, s->pending_exception, s->pending_tval);
      }
      /* we exit because XLEN may have changed */
   done_interp:
  the_end:
      s->insn_counter = GET_INSN_COUNTER();
  #if 0
      printf("done interp %lx int=%x mstatus=%lx prv=%d\n",
             (uint64_t)s->insn_counter, s->mip & s->mie, (uint64_t)s->mstatus,
             s->priv);
  #endif
  }
  
  #undef uintx_t
  #undef intx_t
  #undef XLEN
  #undef OP_A
  