+ case CSR_FFLAGS:
+ require_fp;
+ if (!supports_extension('F'))
+ break;
+ return state.fflags;
+ case CSR_FRM:
+ require_fp;
+ if (!supports_extension('F'))
+ break;
+ return state.frm;
+ case CSR_FCSR:
+ require_fp;
+ if (!supports_extension('F'))
+ break;
+ return (state.fflags << FSR_AEXC_SHIFT) | (state.frm << FSR_RD_SHIFT);
+ case CSR_TIME:
+ case CSR_INSTRET:
+ case CSR_CYCLE:
+ if ((state.mucounteren >> (which & (xlen-1))) & 1)
+ return get_csr(which + (CSR_MCYCLE - CSR_CYCLE));
+ break;
+ case CSR_STIME:
+ case CSR_SINSTRET:
+ case CSR_SCYCLE:
+ if ((state.mscounteren >> (which & (xlen-1))) & 1)
+ return get_csr(which + (CSR_MCYCLE - CSR_SCYCLE));
+ break;
+ case CSR_MUCOUNTEREN: return state.mucounteren;
+ case CSR_MSCOUNTEREN: return state.mscounteren;
+ case CSR_MUCYCLE_DELTA: return 0;
+ case CSR_MUTIME_DELTA: return 0;
+ case CSR_MUINSTRET_DELTA: return 0;
+ case CSR_MSCYCLE_DELTA: return 0;
+ case CSR_MSTIME_DELTA: return 0;
+ case CSR_MSINSTRET_DELTA: return 0;
+ case CSR_MUCYCLE_DELTAH: if (xlen > 32) break; else return 0;
+ case CSR_MUTIME_DELTAH: if (xlen > 32) break; else return 0;
+ case CSR_MUINSTRET_DELTAH: if (xlen > 32) break; else return 0;
+ case CSR_MSCYCLE_DELTAH: if (xlen > 32) break; else return 0;
+ case CSR_MSTIME_DELTAH: if (xlen > 32) break; else return 0;
+ case CSR_MSINSTRET_DELTAH: if (xlen > 32) break; else return 0;
+ case CSR_MCYCLE: return state.minstret;
+ case CSR_MINSTRET: return state.minstret;
+ case CSR_MCYCLEH: if (xlen > 32) break; else return state.minstret >> 32;
+ case CSR_MINSTRETH: if (xlen > 32) break; else return state.minstret >> 32;
+ case CSR_SSTATUS: {
+ reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS
+ | SSTATUS_XS | SSTATUS_PUM;
+ reg_t sstatus = state.mstatus & mask;
+ if ((sstatus & SSTATUS_FS) == SSTATUS_FS ||
+ (sstatus & SSTATUS_XS) == SSTATUS_XS)
+ sstatus |= (xlen == 32 ? SSTATUS32_SD : SSTATUS64_SD);
+ return sstatus;
+ }
+ case CSR_SIP: return state.mip & state.mideleg;
+ case CSR_SIE: return state.mie & state.mideleg;
+ case CSR_SEPC: return state.sepc;
+ case CSR_SBADADDR: return state.sbadaddr;
+ case CSR_STVEC: return state.stvec;
+ case CSR_SCAUSE:
+ if (max_xlen > xlen)
+ return state.scause | ((state.scause >> (max_xlen-1)) << (xlen-1));
+ return state.scause;
+ case CSR_SPTBR: return state.sptbr;
+ case CSR_SASID: return 0;
+ case CSR_SSCRATCH: return state.sscratch;
+ case CSR_MSTATUS: return state.mstatus;
+ case CSR_MIP: return state.mip;
+ case CSR_MIPI: return 0;
+ case CSR_MIE: return state.mie;
+ case CSR_MEPC: return state.mepc;
+ case CSR_MSCRATCH: return state.mscratch;
+ case CSR_MCAUSE: return state.mcause;
+ case CSR_MBADADDR: return state.mbadaddr;
+ case CSR_MISA: return isa;
+ case CSR_MARCHID: return 0;
+ case CSR_MIMPID: return 0;
+ case CSR_MVENDORID: return 0;
+ case CSR_MHARTID: return id;
+ case CSR_MTVEC: return state.mtvec;
+ case CSR_MEDELEG: return state.medeleg;
+ case CSR_MIDELEG: return state.mideleg;
+ }
+ throw trap_illegal_instruction();
+}
+
+reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc)
+{
+ throw trap_illegal_instruction();
+}
+
+insn_func_t processor_t::decode_insn(insn_t insn)
+{
+ // look up opcode in hash table
+ size_t idx = insn.bits() % OPCODE_CACHE_SIZE;
+ insn_desc_t desc = opcode_cache[idx];
+
+ if (unlikely(insn.bits() != desc.match)) {
+ // fall back to linear search
+ insn_desc_t* p = &instructions[0];
+ while ((insn.bits() & p->mask) != p->match)
+ p++;
+ desc = *p;
+
+ if (p->mask != 0 && p > &instructions[0]) {
+ if (p->match != (p-1)->match && p->match != (p+1)->match) {
+ // move to front of opcode list to reduce miss penalty
+ while (--p >= &instructions[0])
+ *(p+1) = *p;
+ instructions[0] = desc;
+ }
+ }
+
+ opcode_cache[idx] = desc;
+ opcode_cache[idx].match = insn.bits();
+ }
+
+ return xlen == 64 ? desc.rv64 : desc.rv32;
+}
+
+void processor_t::register_insn(insn_desc_t desc)
+{
+ instructions.push_back(desc);
+}
+
+void processor_t::build_opcode_map()
+{
+ struct cmp {
+ bool operator()(const insn_desc_t& lhs, const insn_desc_t& rhs) {
+ if (lhs.match == rhs.match)
+ return lhs.mask > rhs.mask;
+ return lhs.match > rhs.match;
+ }
+ };
+ std::sort(instructions.begin(), instructions.end(), cmp());
+
+ for (size_t i = 0; i < OPCODE_CACHE_SIZE; i++)
+ opcode_cache[i] = {1, 0, &illegal_instruction, &illegal_instruction};
+}
+
+void processor_t::register_extension(extension_t* x)
+{
+ for (auto insn : x->get_instructions())
+ register_insn(insn);
+ build_opcode_map();
+ for (auto disasm_insn : x->get_disasms())
+ disassembler->add_insn(disasm_insn);
+ if (ext != NULL)
+ throw std::logic_error("only one extension may be registered");
+ ext = x;
+ x->set_processor(this);
+}
+
+void processor_t::register_base_instructions()
+{
+ #define DECLARE_INSN(name, match, mask) \
+ insn_bits_t name##_match = (match), name##_mask = (mask);
+ #include "encoding.h"
+ #undef DECLARE_INSN
+
+ #define DEFINE_INSN(name) \
+ REGISTER_INSN(this, name, name##_match, name##_mask)
+ #include "insn_list.h"
+ #undef DEFINE_INSN
+
+ register_insn({0, 0, &illegal_instruction, &illegal_instruction});
+ build_opcode_map();
+}
+
+bool processor_t::load(reg_t addr, size_t len, uint8_t* bytes)
+{
+ try {
+ auto res = get_csr(addr / (max_xlen / 8));
+ memcpy(bytes, &res, len);
+ return true;
+ } catch (trap_illegal_instruction& t) {
+ return false;
+ }
+}
+
+bool processor_t::store(reg_t addr, size_t len, const uint8_t* bytes)
+{
+ try {
+ reg_t value = 0;
+ memcpy(&value, bytes, len);
+ set_csr(addr / (max_xlen / 8), value);
+ return true;
+ } catch (trap_illegal_instruction& t) {
+ return false;