+ 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_INSTRET:
+ case CSR_CYCLE:
+ if (ctr_ok)
+ return state.minstret;
+ break;
+ case CSR_MINSTRET:
+ case CSR_MCYCLE:
+ return state.minstret;
+ case CSR_MINSTRETH:
+ case CSR_MCYCLEH:
+ if (xlen == 32)
+ return state.minstret >> 32;
+ break;
+ case CSR_SCOUNTEREN: return state.scounteren;
+ case CSR_MCOUNTEREN: return state.mcounteren;
+ case CSR_SSTATUS: {
+ reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS
+ | SSTATUS_XS | SSTATUS_SUM | SSTATUS_UXL;
+ 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:
+ if (get_field(state.mstatus, MSTATUS_TVM))
+ require_privilege(PRV_M);
+ return state.sptbr;
+ case CSR_SSCRATCH: return state.sscratch;
+ case CSR_MSTATUS: return state.mstatus;
+ case CSR_MIP: return state.mip;
+ 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;
+ case CSR_TSELECT: return state.tselect;
+ case CSR_TDATA1:
+ if (state.tselect < state.num_triggers) {
+ reg_t v = 0;
+ mcontrol_t *mc = &state.mcontrol[state.tselect];
+ v = set_field(v, MCONTROL_TYPE(xlen), mc->type);
+ v = set_field(v, MCONTROL_DMODE(xlen), mc->dmode);
+ v = set_field(v, MCONTROL_MASKMAX(xlen), mc->maskmax);
+ v = set_field(v, MCONTROL_SELECT, mc->select);
+ v = set_field(v, MCONTROL_TIMING, mc->timing);
+ v = set_field(v, MCONTROL_ACTION, mc->action);
+ v = set_field(v, MCONTROL_CHAIN, mc->chain);
+ v = set_field(v, MCONTROL_MATCH, mc->match);
+ v = set_field(v, MCONTROL_M, mc->m);
+ v = set_field(v, MCONTROL_H, mc->h);
+ v = set_field(v, MCONTROL_S, mc->s);
+ v = set_field(v, MCONTROL_U, mc->u);
+ v = set_field(v, MCONTROL_EXECUTE, mc->execute);
+ v = set_field(v, MCONTROL_STORE, mc->store);
+ v = set_field(v, MCONTROL_LOAD, mc->load);
+ return v;
+ } else {
+ return 0;
+ }
+ break;
+ case CSR_TDATA2:
+ if (state.tselect < state.num_triggers) {
+ return state.tdata2[state.tselect];
+ } else {
+ return 0;
+ }
+ break;
+ case CSR_TDATA3: return 0;
+ case CSR_DCSR:
+ {
+ uint32_t v = 0;
+ v = set_field(v, DCSR_XDEBUGVER, 1);
+ v = set_field(v, DCSR_EBREAKM, state.dcsr.ebreakm);
+ v = set_field(v, DCSR_EBREAKH, state.dcsr.ebreakh);
+ v = set_field(v, DCSR_EBREAKS, state.dcsr.ebreaks);
+ v = set_field(v, DCSR_EBREAKU, state.dcsr.ebreaku);
+ v = set_field(v, DCSR_STOPCYCLE, 0);
+ v = set_field(v, DCSR_STOPTIME, 0);
+ v = set_field(v, DCSR_CAUSE, state.dcsr.cause);
+ v = set_field(v, DCSR_STEP, state.dcsr.step);
+ v = set_field(v, DCSR_PRV, state.dcsr.prv);
+ return v;
+ }
+ case CSR_DPC:
+ return state.dpc;
+ case CSR_DSCRATCH:
+ return state.dscratch;
+ }
+ throw trap_illegal_instruction(0);
+}
+
+reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc)
+{
+ throw trap_illegal_instruction(0);
+}
+
+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] = {0, 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)
+{
+ switch (addr)
+ {
+ case 0:
+ if (len <= 4) {
+ memset(bytes, 0, len);
+ bytes[0] = get_field(state.mip, MIP_MSIP);
+ return true;
+ }
+ break;