From 06ba734f341a518d7181dfe38c8635b8bd53a02f Mon Sep 17 00:00:00 2001 From: Toni Date: Tue, 10 Mar 2026 13:53:13 +0100 Subject: [PATCH] disassemble printf --- README.md | 2 +- riscv64.cc | 288 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 242 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 32c6e6f..50b36ef 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Emulators written in C and C++ -* `riscv64.cc` - [RV64IM instruction set](https://en.wikipedia.org/wiki/RISC-V) (more extensions coming soon-ish) +* `riscv64.cc` - [RV64IMAC instruction set](https://en.wikipedia.org/wiki/RISC-V) (more extensions coming soon-ish) * `chip8.c` - [CHIP8](https://en.wikipedia.org/wiki/CHIP-8) * `mos6502.c` - [MOS 6502](https://en.wikipedia.org/wiki/MOS_Technology_6502) diff --git a/riscv64.cc b/riscv64.cc index 825d405..31914f9 100644 --- a/riscv64.cc +++ b/riscv64.cc @@ -1,5 +1,6 @@ // https://docs.riscv.org/reference/isa/unpriv/rv-32-64g.html // https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf +// TODO: convert C instructions to normal ones at parse time? #if !defined(__cplusplus) || __cplusplus < 202302L #error "C++23 or later is required. Either get a newer compiler " \ @@ -7,11 +8,11 @@ "instead of and remove this check." #endif +#include #include #include #include #include -#include #include #include #include @@ -67,7 +68,9 @@ enum Op { C_BEQZ, C_BNEZ, C_EBREAK, + C_FLD, C_FLDSP, + C_FSDSP, C_J, C_JALR, C_JR, @@ -76,6 +79,7 @@ enum Op { C_LI, C_LUI, C_LW, + C_LWSP, C_MV, C_OR, C_SD, @@ -88,13 +92,28 @@ enum Op { C_SW, C_SWSP, C_XOR, + CSRRS, + CSRRSI, DIV, DIVU, DIVUW, DIVW, ECALL, + FCLASS_D, + FCVT_D_W, + FCVT_D_WU, FENCE, FENCE_TSO, + FLD, + FMUL_D, + FMV_D_X, + FMV_W_X, + FMV_X_D, + FSD, + FSGNJ_D, + FSGNJN_D, + FSGNJX_D, + FSW, JAL, JALR, LB, @@ -144,6 +163,8 @@ enum Op { SW, XOR, XORI, + + NUM_OPS }; struct Ins { @@ -183,7 +204,9 @@ enum class Format { CR1, CL, R_ATOMIC, - R_ATOMIC_LR + R_ATOMIC_LR, + CSR, + CSRI }; struct OpDef { @@ -191,7 +214,7 @@ struct OpDef { Format format; }; -static constexpr std::array OP_TABLE = {{ +static constexpr auto OP_TABLE = std::to_array({ {"???", Format::NONE}, {"add", Format::R}, {"addi", Format::I}, @@ -218,7 +241,9 @@ static constexpr std::array OP_TABLE = {{ {"c.beqz", Format::CB}, {"c.bnez", Format::CB}, {"c.ebreak", Format::NONE}, - {"c.fldsp", Format::CSS}, + {"c.fld", Format::CL}, + {"c.fldsp", Format::CI}, + {"c.fsdsp", Format::CSS}, {"c.j", Format::CJ}, {"c.jalr", Format::CR1}, {"c.jr", Format::CR1}, @@ -227,6 +252,7 @@ static constexpr std::array OP_TABLE = {{ {"c.li", Format::CI}, {"c.lui", Format::CI}, {"c.lw", Format::CL}, + {"c.lwsp", Format::CL}, {"c.mv", Format::CR}, {"c.or", Format::CR}, {"c.sd", Format::S}, @@ -239,13 +265,28 @@ static constexpr std::array OP_TABLE = {{ {"c.sw", Format::S}, {"c.swsp", Format::CSS}, {"c.xor", Format::CR}, + {"csrrs", Format::CSR}, + {"csrrsi", Format::CSRI}, {"div", Format::R}, {"divu", Format::R}, {"divuw", Format::R}, {"divw", Format::R}, {"ecall", Format::NONE}, + {"fclass.d", Format::R}, + {"fcvt.d.w", Format::R}, + {"fcvt.d.wu", Format::R}, {"fence", Format::NONE}, {"fence.tso", Format::NONE}, + {"fld", Format::I}, + {"fmul.d", Format::R}, + {"fmv.d.x", Format::R}, + {"fmv.x.d", Format::R}, + {"fmv.w.x", Format::R}, + {"fsw", Format::S}, + {"fsd", Format::S}, + {"fsgnj.d", Format::R}, + {"fsgnjn.d", Format::R}, + {"fsgnjx.d", Format::R}, {"jal", Format::J}, {"jalr", Format::I}, {"lb", Format::I_LOAD}, @@ -295,7 +336,9 @@ static constexpr std::array OP_TABLE = {{ {"sw", Format::S}, {"xor", Format::R}, {"xori", Format::I}, -}}; +}); + +static_assert(OP_TABLE.size() == NUM_OPS, "len(OP_TABLE) != len(Op::*)"); class RISCV64 { public: @@ -324,6 +367,7 @@ public: GElf_Phdr phdr; gelf_getphdr(elf, i, &phdr); if (phdr.p_type == PT_LOAD) { + // TODO: probably should disassemble all of those instead of just .text? std::copy_n(exe_bytes.data() + phdr.p_offset, phdr.p_filesz, m_memory + phdr.p_vaddr); } @@ -358,10 +402,18 @@ public: } } + // TODO: the registers in floating-point instructions should use the f0-f31 + // registers but that would require rewriting a lot of stuff and its not gonna + // be a problem in execution so we'll live with that for now void disassemble_ins(Ins ins) { + assert((u64)ins.op < OP_TABLE.size()); + const OpDef &def = OP_TABLE[ins.op]; switch (def.format) { + case Format::NONE: + std::println("{}", def.mnemonic); + break; case Format::R: std::println("{} {}, {}, {}", def.mnemonic, REGS[ins.rd], REGS[ins.rs1], REGS[ins.rs2]); @@ -421,8 +473,13 @@ public: std::println("{} {}, {}, ({})", def.mnemonic, REGS[ins.rd], REGS[ins.rs2], REGS[ins.rs1]); break; - case Format::NONE: - std::println("{}", def.mnemonic); + case Format::CSR: + std::println("{} {}, {}, {}", def.mnemonic, REGS[ins.rd], ins.imm, + REGS[ins.rs1]); + break; + case Format::CSRI: + std::println("{} {}, {}, {}", def.mnemonic, REGS[ins.rd], ins.imm, + ins.rs1); break; } } @@ -594,11 +651,11 @@ public: m_regs[i.rd] = i.imm; }; break; case Op::C_LUI: { - m_regs[i.rd] = (int64_t)(uint64_t)((int64_t)i.imm) << 12; + m_regs[i.rd] = (i64)(i32)((u32)i.imm << 12); }; break; case Op::C_LW: { u64 addr = m_regs[i.rs1] + i.imm; - m_regs[i.rd] = *(u32 *)&m_memory[addr]; + m_regs[i.rd] = (i64) * (i32 *)&m_memory[addr]; }; break; case Op::C_MV: { m_regs[i.rd] = m_regs[i.rs2]; @@ -798,31 +855,43 @@ public: m_regs[i.rd] = m_regs[i.rs1] * m_regs[i.rs2]; }; break; case Op::MULH: { - i64 a_hi = m_regs[i.rs1] >> 32; - i64 b_hi = m_regs[i.rs2] >> 32; - u64 a_lo = (u32)m_regs[i.rs1]; - u64 b_lo = (u32)m_regs[i.rs2]; - u64 p0 = a_lo * b_lo; - i64 p1 = a_hi * b_lo; - i64 p2 = b_hi * a_lo; - i64 p3 = a_hi * b_hi; - i64 carry = (p0 >> 32); - i64 mid = p1 + p2 + carry; - m_regs[i.rd] = p3 + (mid >> 32); + i64 rs1 = m_regs[i.rs1]; + i64 rs2 = m_regs[i.rs2]; + u64 u = rs1; + u64 v = rs2; + u64 u0 = u & 0xffffffff; + u64 u1 = u >> 32; + u64 v0 = v & 0xffffffff; + u64 v1 = v >> 32; + u64 t = u0 * v0; + u64 k = t >> 32; + t = u1 * v0 + k; + u64 w1 = t & 0xffffffff; + u64 w2 = t >> 32; + t = u0 * v1 + w1; + k = t >> 32; + u64 res = u1 * v1 + w2 + k; + if (rs1 < 0) + res -= u64(rs2); + if (rs2 < 0) + res -= u64(rs1); + m_regs[i.rd] = (i64)res; }; break; case Op::MULHU: { u64 a = m_regs[i.rs1]; u64 b = m_regs[i.rs2]; - u64 a_lo = a & 0xffffffff; - u64 a_hi = a >> 32; - u64 b_lo = b & 0xffffffff; - u64 b_hi = b >> 32; - u64 lo_lo = a_lo * b_lo; - u64 hi_lo = a_hi * b_lo; - u64 lo_hi = a_lo * b_hi; - u64 hi_hi = a_hi * b_hi; - u64 mid = (lo_lo >> 32) + (hi_lo & 0xffffffff) + (lo_hi & 0xffffffff); - m_regs[i.rd] = hi_hi + (hi_lo >> 32) + (lo_hi >> 32) + (mid >> 32); + u64 a0 = a & 0xffffffff; + u64 a1 = a >> 32; + u64 b0 = b & 0xffffffff; + u64 b1 = b >> 32; + u64 t = a0 * b0; + u64 k = t >> 32; + t = a1 * b0 + k; + u64 w1 = t & 0xffffffff; + u64 w2 = t >> 32; + t = a0 * b1 + w1; + k = t >> 32; + m_regs[i.rd] = a1 * b1 + w2 + k; }; break; case Op::MULW: { m_regs[i.rd] = (i32)(m_regs[i.rs1] * m_regs[i.rs2]); @@ -994,6 +1063,12 @@ private: (((raw >> 6) & 0b1) << 2) | (((raw >> 5) & 0b1) << 3); i.op = Op::C_ADDI4SPN; }; break; + case 0b001: { + i.rd = ((raw >> 2) & 0b111) + 8; + i.rs1 = ((raw >> 7) & 0b111) + 8; + i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 5) & 0b11) << 6); + i.op = Op::C_FLD; + }; break; case 0b010: { i.rd = ((raw >> 2) & 0b111) + 8; i.rs1 = ((raw >> 7) & 0b111) + 8; @@ -1032,7 +1107,7 @@ private: case 0b000: { i.imm = ((raw >> 2) & 0b11111) | (((raw >> 12) & 0b1) << 5); i.imm = (i.imm << 26) >> 26; - i.op = Op::C_ADDI; + i.op = static_cast(254); // TODO: Op::C_ADDI; }; break; case 0b001: { i.rs1 = i.rd; @@ -1176,6 +1251,12 @@ private: (((raw >> 2) & 0b111) << 6); i.op = Op::C_FLDSP; }; break; + case 0b010: { + i.rs1 = 2; + i.imm = (((raw >> 2) & 0b11) << 6) | (((raw >> 12) & 0b1) << 5) | + (((raw >> 4) & 0b111) << 2); + i.op = Op::C_LWSP; + }; break; case 0b011: { i.rs1 = 2; i.imm = (((raw >> 12) & 0b1) << 5) | (((raw >> 5) & 0b11) << 3) | @@ -1212,6 +1293,12 @@ private: } } }; break; + case 0b101: { + i.rs2 = (raw >> 2) & 0b11111; + i.rs1 = 2; + i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 7) & 0b111) << 6); + i.op = Op::C_FSDSP; + }; break; case 0b110: { i.rs2 = (raw >> 2) & 0b11111; i.rs1 = 2; @@ -1290,8 +1377,7 @@ private: if (funct6 == 0b000000) { i.op = Op::SLLI; } else { - std::println(stderr, - "I-type 1: funct3=001: unrecognized funct6: {:b}", + std::println(stderr, "0010011: funct3=001: unrecognized funct6: {:b}", funct6); exit(1); } @@ -1310,8 +1396,7 @@ private: } else if (funct6 == 0b010000) { i.op = Op::SRAI; } else { - std::println(stderr, - "I-type 1: funct3=101: unrecognized funct6: {:b}", + std::println(stderr, "0010011: funct3=101: unrecognized funct6: {:b}", funct6); exit(1); } @@ -1320,7 +1405,7 @@ private: } else if (funct3 == 0b111) { i.op = Op::ANDI; } else { - std::println(stderr, "I-type 1: unrecognized funct3: {:03b}", funct3); + std::println(stderr, "0010011: unrecognized funct3: {:03b}", funct3); exit(1); } }; break; @@ -1345,7 +1430,7 @@ private: } else if (funct3 == 0b110) { i.op = Op::LWU; } else { - std::println(stderr, "I-type 2: unrecognized funct3: {:03b}", funct3); + std::println(stderr, "0000011: unrecognized funct3: {:03b}", funct3); exit(1); } }; break; @@ -1359,18 +1444,26 @@ private: u8 funct3 = (raw >> 12) & 0b111; i.rd = (raw >> 7) & 0b11111; i.rs1 = (raw >> 15) & 0b11111; - i.imm = ((i32)raw) >> 20; + i.imm = (raw >> 20) & 0b111111111111; - if (funct3 == 0b000) { - if (i.imm == 0b000000000000) { + switch (funct3) { + case 0b000: { + if (i.imm == 0) { i.op = Op::ECALL; } else { - std::println(stderr, "I-type 4: funct3=000 unrecognized imm: {:b}", + std::println(stderr, "1110011: funct3=000: unrecognized imm: {:b}", i.imm); exit(1); } - } else { - std::println(stderr, "I-type 4: unrecognized funct3: {:03b}", funct3); + }; break; + case 0b010: { + i.op = Op::CSRRS; + }; break; + case 0b110: { + i.op = Op::CSRRSI; + }; break; + default: + std::println(stderr, "1110011: unrecognized funct3: {:03b}", funct3); exit(1); } }; break; @@ -1607,12 +1700,113 @@ private: } }; break; case 0b0000111: { - std::println(stderr, "F extension not implemented yet."); - exit(1); + u8 funct3 = (raw >> 12) & 0b111; + + switch (funct3) { + case 0b011: { + i.rd = (raw >> 7) & 0b11111; + i.rs1 = (raw >> 15) & 0b11111; + i.imm = (i32)raw >> 20; + i.op = Op::FLD; + }; break; + default: + std::println(stderr, "0000111: unrecognized funct3: {:03b}", funct3); + exit(1); + } + }; break; + case 0b1010011: { + u8 funct3 = (raw >> 12) & 0b111; + u8 funct7 = (raw >> 25) & 0b1111111; + i.rs1 = (raw >> 15) & 0b11111; + i.rs2 = (raw >> 20) & 0b11111; + + switch (funct7) { + case 0b1111001: { + i.rs1 = (raw >> 15) & 0b11111; + i.op = Op::FMV_D_X; + }; break; + case 0b1101001: { + if (i.rs2 == 0b00000) { + i.op = Op::FCVT_D_W; + } else if (i.rs2 == 0b00001) { + i.op = Op::FCVT_D_WU; + } else { + std::println( + stderr, "1010011: funct7=1101001: unrecognized rs2: {:b}", i.rs2); + exit(1); + } + }; break; + case 0b0001001: { + i.op = Op::FMUL_D; + }; break; + case 0b0010001: { + switch (funct3) { + case 0b000: + i.op = Op::FSGNJ_D; + break; + case 0b001: + i.op = Op::FSGNJN_D; + break; + case 0b010: + i.op = Op::FSGNJX_D; + break; + default: + std::println(stderr, + "1010011: funct7=0010001: unrecognized funct3: {:03b}", + funct3); + exit(1); + } + }; break; + case 0b1110001: { + switch (funct3) { + case 0b000: { + i.op = Op::FMV_X_D; + }; break; + case 0b001: { + i.op = Op::FCLASS_D; + }; break; + default: { + std::println(stderr, + "1010011: funct7=1110001: unrecognized funct3: {:03b}", + funct3); + exit(1); + }; break; + } + }; break; + case 0b1111000: { + i.op = Op::FMV_W_X; + }; break; + default: { + std::println(stderr, "1010011: unrecognized funct7: {:07b}", funct7); + exit(1); + }; break; + } }; break; case 0b0100111: { - std::println(stderr, "F extension not implemented yet."); - exit(1); + u8 funct3 = (raw >> 12) & 0b111; + + switch (funct3) { + case 0b010: { + i.rs1 = (raw >> 15) & 0b11111; + i.rs2 = (raw >> 20) & 0b11111; + i32 imm_hi = (i32)raw >> 25; + i32 imm_lo = (raw >> 7) & 0b11111; + i.imm = (imm_hi << 5) | imm_lo; + i.op = Op::FSW; + }; break; + case 0b011: { + i.rs1 = (raw >> 15) & 0b11111; + i.rs2 = (raw >> 20) & 0b11111; + i32 imm_hi = (i32)raw >> 25; + i32 imm_lo = (raw >> 7) & 0b11111; + i.imm = (imm_hi << 5) | imm_lo; + i.op = Op::FSD; + }; break; + default: { + std::println(stderr, "0100111: unrecognized funct3: {:03b}", funct3); + exit(1); + }; break; + } }; break; case 0b0100011: { u8 funct3 = (raw >> 12) & 0b111;