"""RV64IMAC instruction decoder (pure Python, stdlib only). The decoder canonicalises every compressed instruction into the equivalent 32-bit operation, so breakpoints, single-step and watchpoints behave uniformly regardless of encoding. ``decode16``/``decode32`` return a 7-tuple (kind, sub, rd, rs1, rs2, imm, next_pc) where ``next_pc`` is the fall-through (or branch/jump target for the unconditional control transfers), ``imm`` is already sign/zero-normalised for its use, and branch/jump targets are absolute virtual addresses. Results are cached per PC by the Machine; the cache is dropped when the debugger writes memory (M1 does not model self-modifying code). """ from __future__ import annotations MASK64 = (1 << 64) - 1 # Instruction kinds. ( K_LUI, K_AUIPC, K_JAL, K_JALR, K_BRANCH, K_LOAD, K_STORE, K_OPI, K_OP, K_OPI32, K_OP32, K_FENCE, K_CSR, K_ECALL, K_EBREAK, K_MRET, K_AMO, K_ILLEGAL, ) = range(18) # ALU ops shared by K_OP and (width-truncated) K_OP32. ( A_ADD, A_SLL, A_SLT, A_SLTU, A_XOR, A_SRL, A_OR, A_AND, A_SUB, A_SRA, A_MUL, A_MULH, A_MULHSU, A_MULHU, A_DIV, A_DIVU, A_REM, A_REMU, ) = range(18) # K_OPI / K_OPI32 immediate ops (funct3 values, with srai/sraiw offset). I_ADDI, I_SLLI, I_SLTI, I_SLTIU, I_XORI, I_SRLI, I_ORI, I_ANDI = range(8) I_SRAI = 0x15 I_ADDIW, I_SLLIW, I_SRLIW, I_SRAIW = 0, 1, 5, 0x15 # LOAD/STORE funct3. LOAD_SIZE = (1, 2, 4, 8, 1, 2, 4) STORE_SIZE = (1, 2, 4, 8) _ILLEGAL = (K_ILLEGAL, 0, 0, 0, 0, 0, 0) def sext(value: int, bits: int) -> int: sign = 1 << (bits - 1) return (value & (sign - 1)) - (value & sign) def _bits(value: int, hi: int, lo: int) -> int: return (value >> lo) & ((1 << (hi - lo + 1)) - 1) def _op_alu(sub: int) -> int: """Map an R-type funct3 to the shared ALU op code (non-M).""" return { 0: A_ADD, 1: A_SLL, 2: A_SLT, 3: A_SLTU, 4: A_XOR, 5: A_SRL, 6: A_OR, 7: A_AND, }[sub] def decode32(pc: int, insn: int): opcode = insn & 0x7F rd = (insn >> 7) & 0x1F funct3 = (insn >> 12) & 7 rs1 = (insn >> 15) & 0x1F rs2 = (insn >> 20) & 0x1F funct7 = (insn >> 25) & 0x7F nxt = pc + 4 if opcode == 0x37: # LUI return (K_LUI, 0, rd, 0, 0, (insn & 0xFFFFF000) & MASK64, nxt) if opcode == 0x17: # AUIPC return (K_AUIPC, 0, rd, 0, 0, sext(insn & 0xFFFFF000, 32) & MASK64, nxt) if opcode == 0x6F: # JAL imm = sext( (_bits(insn, 31, 31) << 20) | (_bits(insn, 30, 21) << 1) | (_bits(insn, 20, 20) << 11) | (_bits(insn, 19, 12) << 12), 21, ) return (K_JAL, 0, rd, 0, 0, (pc + imm) & MASK64, nxt) if opcode == 0x67: # JALR return (K_JALR, funct3, rd, rs1, 0, sext(_bits(insn, 31, 20), 12), nxt) if opcode == 0x63: # BRANCH imm = sext( (_bits(insn, 31, 31) << 12) | (_bits(insn, 30, 25) << 5) | (_bits(insn, 11, 8) << 1) | (_bits(insn, 7, 7) << 11), 13, ) return (K_BRANCH, funct3, 0, rs1, rs2, (pc + imm) & MASK64, nxt) if opcode == 0x03: # LOAD if funct3 == 7: return _ILLEGAL return (K_LOAD, funct3, rd, rs1, 0, sext(_bits(insn, 31, 20), 12), nxt) if opcode == 0x23: # STORE if funct3 > 3: return _ILLEGAL imm = sext((_bits(insn, 31, 25) << 5) | _bits(insn, 11, 7), 12) return (K_STORE, funct3, 0, rs1, rs2, imm, nxt) if opcode == 0x13: # OP-IMM shamt = (insn >> 20) & 0x3F top = (insn >> 26) & 0x3F if funct3 == 1: if top: return _ILLEGAL return (K_OPI, I_SLLI, rd, rs1, 0, shamt, nxt) if funct3 == 5: if top == 0: sub = I_SRLI elif top == 0x10: sub = I_SRAI else: return _ILLEGAL return (K_OPI, sub, rd, rs1, 0, shamt, nxt) return (K_OPI, funct3, rd, rs1, 0, sext(_bits(insn, 31, 20), 12), nxt) if opcode == 0x33: # OP if funct7 == 0x01: # M extension return (K_OP, A_MUL + funct3, rd, rs1, rs2, 0, nxt) if funct7 == 0x00: sub = _op_alu(funct3) elif funct7 == 0x20 and funct3 == 0: sub = A_SUB elif funct7 == 0x20 and funct3 == 5: sub = A_SRA else: return _ILLEGAL return (K_OP, sub, rd, rs1, rs2, 0, nxt) if opcode == 0x1B: # OP-IMM-32 if funct3 == 0: return (K_OPI32, I_ADDIW, rd, rs1, 0, sext(_bits(insn, 31, 20), 12), nxt) if funct3 == 1: if (insn >> 25) & 0x7F: return _ILLEGAL return (K_OPI32, I_SLLIW, rd, rs1, 0, (insn >> 20) & 0x1F, nxt) if funct3 == 5: top = (insn >> 25) & 0x7F if top == 0: sub = I_SRLIW elif top == 0x20: sub = I_SRAIW else: return _ILLEGAL return (K_OPI32, sub, rd, rs1, 0, (insn >> 20) & 0x1F, nxt) return _ILLEGAL if opcode == 0x3B: # OP-32 if funct7 == 0x00: if funct3 == 0: sub = A_ADD elif funct3 == 1: sub = A_SLL elif funct3 == 5: sub = A_SRL else: return _ILLEGAL elif funct7 == 0x20: if funct3 == 0: sub = A_SUB elif funct3 == 5: sub = A_SRA else: return _ILLEGAL elif funct7 == 0x01: if funct3 == 0: sub = A_MUL elif funct3 == 4: sub = A_DIV elif funct3 == 5: sub = A_DIVU elif funct3 == 6: sub = A_REM elif funct3 == 7: sub = A_REMU else: return _ILLEGAL else: return _ILLEGAL return (K_OP32, sub, rd, rs1, rs2, 0, nxt) if opcode == 0x0F: # FENCE / FENCE.I: ordering is not modelled (single core) if funct3 in (0, 1): return (K_FENCE, 0, 0, 0, 0, 0, nxt) return _ILLEGAL if opcode == 0x73: # SYSTEM if funct3 == 0: imm12 = insn >> 20 if imm12 == 0: return (K_ECALL, 0, 0, 0, 0, 0, nxt) if imm12 == 1: return (K_EBREAK, 0, 0, 0, 0, 0, nxt) if imm12 in (0x302, 0x102): # mret / sret (treated alike, M1) return (K_MRET, 0, 0, 0, 0, 0, nxt) return _ILLEGAL return (K_CSR, funct3, rd, rs1, 0, insn >> 20, nxt) if opcode == 0x2F: # AMO if funct3 not in (2, 3): return _ILLEGAL funct5 = funct7 >> 2 if funct5 not in (0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C): return _ILLEGAL sub = funct5 | (0x40 if funct3 == 3 else 0) return (K_AMO, sub, rd, rs1, rs2, 0, nxt) return _ILLEGAL def _rvc_j_imm(insn: int) -> int: return sext( (_bits(insn, 12, 12) << 11) | (_bits(insn, 11, 11) << 4) | (_bits(insn, 10, 9) << 8) | (_bits(insn, 8, 8) << 10) | (_bits(insn, 7, 7) << 6) | (_bits(insn, 6, 6) << 7) | (_bits(insn, 5, 3) << 1) | (_bits(insn, 2, 2) << 5), 12, ) def _rvc_b_imm(insn: int) -> int: return sext( (_bits(insn, 12, 12) << 8) | (_bits(insn, 11, 10) << 3) | (_bits(insn, 6, 5) << 6) | (_bits(insn, 4, 3) << 1) | (_bits(insn, 2, 2) << 5), 9, ) def decode16(pc: int, insn: int): op = insn & 3 funct3 = (insn >> 13) & 7 nxt = pc + 2 rd = (insn >> 7) & 0x1F if op == 0: if funct3 == 0: # c.addi4spn imm = ( ((insn >> 7) & 0x30) | ((insn >> 1) & 0x3C0) | ((insn >> 4) & 0x4) | ((insn >> 2) & 0x8) ) if imm == 0: return _ILLEGAL return (K_OPI, I_ADDI, 8 + ((insn >> 2) & 7), 2, 0, imm, nxt) if funct3 == 2: # c.lw imm = ((insn >> 7) & 0x38) | ((insn >> 4) & 0x4) | ((insn << 1) & 0x40) return (K_LOAD, 2, 8 + ((insn >> 2) & 7), 8 + ((insn >> 7) & 7), 0, imm, nxt) if funct3 == 3: # c.ld imm = ((insn >> 7) & 0x38) | ((insn << 1) & 0xC0) return (K_LOAD, 3, 8 + ((insn >> 2) & 7), 8 + ((insn >> 7) & 7), 0, imm, nxt) if funct3 == 6: # c.sw imm = ((insn >> 7) & 0x38) | ((insn >> 4) & 0x4) | ((insn << 1) & 0x40) return (K_STORE, 2, 0, 8 + ((insn >> 7) & 7), 8 + ((insn >> 2) & 7), imm, nxt) if funct3 == 7: # c.sd imm = ((insn >> 7) & 0x38) | ((insn << 1) & 0xC0) return (K_STORE, 3, 0, 8 + ((insn >> 7) & 7), 8 + ((insn >> 2) & 7), imm, nxt) return _ILLEGAL if op == 1: if funct3 == 0: # c.addi / c.nop imm = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6) return (K_OPI, I_ADDI, rd, rd, 0, imm, nxt) if funct3 == 1: # c.addiw if rd == 0: return _ILLEGAL imm = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6) return (K_OPI32, I_ADDIW, rd, rd, 0, imm, nxt) if funct3 == 2: # c.li imm = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6) return (K_OPI, I_ADDI, rd, 0, 0, imm, nxt) if funct3 == 3: if rd == 2: # c.addi16sp imm = sext( ((insn >> 3) & 0x200) | ((insn >> 2) & 0x10) | ((insn << 1) & 0x40) | ((insn << 4) & 0x180) | ((insn << 3) & 0x20), 10, ) if imm == 0: return _ILLEGAL return (K_OPI, I_ADDI, 2, 2, 0, imm, nxt) imm6 = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6) if rd == 0 or imm6 == 0: # c.lui return _ILLEGAL return (K_LUI, 0, rd, 0, 0, (imm6 << 12) & MASK64, nxt) if funct3 == 4: sub3 = (insn >> 10) & 3 rd_p = 8 + ((insn >> 7) & 7) if sub3 == 0: # c.srli shamt = (_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2) return (K_OPI, I_SRLI, rd_p, rd_p, 0, shamt, nxt) if sub3 == 1: # c.srai shamt = (_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2) return (K_OPI, I_SRAI, rd_p, rd_p, 0, shamt, nxt) if sub3 == 2: # c.andi imm = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6) return (K_OPI, I_ANDI, rd_p, rd_p, 0, imm, nxt) f2 = (insn >> 5) & 3 if _bits(insn, 12, 12) == 0: sub = (A_SUB, A_XOR, A_OR, A_AND)[f2] return (K_OP, sub, rd_p, rd_p, 8 + ((insn >> 2) & 7), 0, nxt) if f2 == 0: # c.subw return (K_OP32, A_SUB, rd_p, rd_p, 8 + ((insn >> 2) & 7), 0, nxt) if f2 == 1: # c.addw return (K_OP32, A_ADD, rd_p, rd_p, 8 + ((insn >> 2) & 7), 0, nxt) return _ILLEGAL if funct3 == 5: # c.j return (K_JAL, 0, 0, 0, 0, (pc + _rvc_j_imm(insn)) & MASK64, nxt) if funct3 == 6: # c.beqz return (K_BRANCH, 0, 0, 8 + ((insn >> 7) & 7), 0, (pc + _rvc_b_imm(insn)) & MASK64, nxt) if funct3 == 7: # c.bnez return (K_BRANCH, 1, 0, 8 + ((insn >> 7) & 7), 0, (pc + _rvc_b_imm(insn)) & MASK64, nxt) return _ILLEGAL if op == 2: if funct3 == 0: # c.slli if rd == 0: return _ILLEGAL shamt = (_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2) return (K_OPI, I_SLLI, rd, rd, 0, shamt, nxt) if funct3 == 2: # c.lwsp if rd == 0: return _ILLEGAL imm = ((insn >> 7) & 0x20) | ((insn >> 2) & 0x1C) | ((insn << 4) & 0xC0) return (K_LOAD, 2, rd, 2, 0, imm, nxt) if funct3 == 3: # c.ldsp if rd == 0: return _ILLEGAL imm = ((insn >> 7) & 0x20) | ((insn >> 2) & 0x18) | ((insn << 4) & 0x1C0) return (K_LOAD, 3, rd, 2, 0, imm, nxt) if funct3 == 4: rs2 = (insn >> 2) & 0x1F if _bits(insn, 12, 12) == 0: if rs2 == 0: # c.jr if rd == 0: return _ILLEGAL return (K_JALR, 0, 0, rd, 0, 0, nxt) if rd == 0: # c.mv return _ILLEGAL return (K_OP, A_ADD, rd, 0, rs2, 0, nxt) if rs2 == 0 and rd == 0: # c.ebreak return (K_EBREAK, 0, 0, 0, 0, 0, nxt) if rs2 == 0: # c.jalr if rd == 0: return _ILLEGAL return (K_JALR, 0, 1, rd, 0, 0, nxt) return (K_OP, A_ADD, rd, rd, rs2, 0, nxt) # c.add if funct3 == 6: # c.swsp imm = ((insn >> 7) & 0x3C) | ((insn >> 1) & 0xC0) return (K_STORE, 2, 0, 2, (insn >> 2) & 0x1F, imm, nxt) if funct3 == 7: # c.sdsp imm = ((insn >> 7) & 0x38) | ((insn >> 1) & 0x1C0) return (K_STORE, 3, 0, 2, (insn >> 2) & 0x1F, imm, nxt) return _ILLEGAL return _ILLEGAL