/* SPDX-License-Identifier: copyleft-next-0.3.1 */ #include #include #include struct disasm_ctx { const uint32_t *buf; size_t pc; size_t len; }; static const char *get_gpr(tri_t x) { switch (x) { case X0: return "x0"; case X1: return "sp"; case X2: return "fp"; case X3: return "gp"; case X4: return "a0"; case X5: return "a1"; case X6: return "a2"; case X7: return "a3"; case X8: return "a4"; case X9: return "a5"; case X10: return "a6"; case X11: return "t0"; case X12: return "t1"; case X13: return "t2"; case X14: return "t3"; case X15: return "t4"; case X16: return "t5"; case X17: return "t6"; case X18: return "s0"; case X19: return "s1"; case X20: return "s2"; case X21: return "s3"; case X22: return "s4"; case X23: return "s5"; case X24: return "s6"; case X25: return "tp"; case X26: return "ra"; case X27: return "a7"; case X28: return "a8"; case X29: return "a9"; case X30: return "a10"; case X31: return "a11"; case X32: return "a12"; case X33: return "a13"; case X34: return "a14"; case X35: return "a15"; case X36: return "a16"; case X37: return "a17"; case X38: return "a18"; case X39: return "a19"; case X40: return "a20"; case X41: return "a21"; case X42: return "a22"; case X43: return "a23"; case X44: return "a24"; case X45: return "t7"; case X46: return "t8"; case X47: return "t9"; case X48: return "t10"; case X49: return "t11"; case X50: return "t12"; case X51: return "t13"; case X52: return "t14"; case X53: return "t15"; case X54: return "t16"; case X55: return "t17"; case X56: return "t18"; case X57: return "t19"; case X58: return "t20"; case X59: return "t21"; case X60: return "t22"; case X61: return "t23"; case X62: return "t24"; case X63: return "s7"; case X64: return "s8"; case X65: return "s9"; case X66: return "s10"; case X67: return "s11"; case X68: return "s12"; case X69: return "s13"; case X70: return "s14"; case X71: return "s15"; case X72: return "s16"; case X73: return "s17"; case X74: return "s18"; case X75: return "s19"; case X76: return "s20"; case X77: return "s21"; case X78: return "s22"; case X79: return "s23"; case X80: return "s24"; default: return "ill"; } } static void *read_object_file(const char *file, size_t *len) { FILE *f = fopen(file, "rb"); if (!f) { fprintf(stderr, "failed opening simulation file %s\n", file); return NULL; } fseek(f, 0, SEEK_END); long s = ftell(f); if (s == LONG_MAX) { fprintf(stderr, "unable to get file size of %s\n", file); return NULL; } if (len) *len = s; fseek(f, 0, SEEK_SET); char *buf = malloc(s); if (!buf) return NULL; fread(buf, s, 1, f); fclose(f); return buf; } #define DEFAULT_PREFIX "%zu\t" static void disasm_lui(struct disasm_ctx *ctx, tri_t i) { tri_t rd, imm18; parse_u(i, &rd, &imm18); char imm[256]; size_t l = tri_fmt_default(imm, 256, imm18); imm[l] = '\0'; printf(DEFAULT_PREFIX "lui %s, %s\n", ctx->pc, get_gpr(rd), imm); } static void disasm_store(struct disasm_ctx *ctx, tri_t i) { tri_t imm4, fn0, rs1, rs2, imm5; parse_s(i, &imm4, &fn0, &rs1, &rs2, &imm5); tri_t imm9 = tri_sl(imm4, 5) | imm5; int w = tri_get_trit(fn0, 4); char *width = NULL; switch (w) { case 0: width = "t"; break; case 1: width = "w"; break; default: width = "ill"; break; } char imm[256]; size_t l = tri_fmt_default(imm, 256, imm9); imm[l] = '\0'; printf(DEFAULT_PREFIX "st %s, %s, %s(%s)\n", ctx->pc, width, get_gpr(rs1), imm, get_gpr(rs2)); } static void disasm_load(struct disasm_ctx *ctx, tri_t i) { tri_t rd, fn0, rs1, imm9; parse_i(i, &rd, &fn0, &rs1, &imm9); int w = tri_get_trit(fn0, 4); char *width = NULL; switch (w) { case 0: width = "t"; break; case 1: width = "w"; break; default: width = "ill"; break; } char imm[256]; size_t l = tri_fmt_default(imm, 256, imm9); imm[l] = '\0'; printf(DEFAULT_PREFIX "ld %s, %s, %s(%s)\n", ctx->pc, width, get_gpr(rd), imm, get_gpr(rs1)); } static void disasm_op_imm(struct disasm_ctx *ctx, tri_t i) { tri_t rd, fn0, rs1, imm9; parse_i(i, &rd, &fn0, &rs1, &imm9); char imm[256]; size_t l = tri_fmt_default(imm, 256, imm9); imm[l] = '\0'; switch (fn0) { case OP_IMM_ADDI: printf(DEFAULT_PREFIX "addi %s, %s, %s\n", ctx->pc, get_gpr(rd), get_gpr(rs1), imm); break; default: printf(DEFAULT_PREFIX "unknown/unimp op_imm\n", ctx->pc); } } static void disasm_op(struct disasm_ctx *ctx, tri_t i) { tri_t rd, fn0, rs1, rs2, fn5; parse_r(i, &rd, &fn0, &rs1, &rs2, &fn5); switch (fn5) { case 0: switch (fn0) { case OP_ADD: printf(DEFAULT_PREFIX "add %s, %s, %s\n", ctx->pc, get_gpr(rd), get_gpr(rs1), get_gpr(rs2)); break; case OP_SUB: printf(DEFAULT_PREFIX "sub %s, %s, %s\n", ctx->pc, get_gpr(rd), get_gpr(rs1), get_gpr(rs2)); break; default: printf(DEFAULT_PREFIX "unknown/unimp op\n", ctx->pc); } break; default: printf(DEFAULT_PREFIX "unknown/unimp op metaclass\n", ctx->pc); } } static void disasm_jalr(struct disasm_ctx *ctx, tri_t i) { tri_t rd, fn0, rs1, imm9; parse_i(i, &rd, &fn0, &rs1, &imm9); char imm[256]; size_t l = tri_fmt_default(imm, 256, imm9); imm[l] = '\0'; switch (fn0) { case 0: printf(DEFAULT_PREFIX "jalr %s, %s(%s)\n", ctx->pc, get_gpr(rd), imm, get_gpr(rs1)); break; default: printf(DEFAULT_PREFIX "unknown/unimp jalr\n", ctx->pc); } } static void disasm_branch(struct disasm_ctx *ctx, tri_t i) { tri_t imm4, fn0, rs1, rs2, imm5; parse_s(i, &imm4, &fn0, &rs1, &rs2, &imm5); tri_t imm9 = tri_sl(imm4, 5) | imm5; char imm[256]; size_t l = tri_fmt_default(imm, 256, imm9); imm[l] = '\0'; switch (fn0) { case BRANCH_BLT: printf(DEFAULT_PREFIX "blt %s, %s, %s\n", ctx->pc, get_gpr(rs1), get_gpr(rs2), imm); break; default: printf(DEFAULT_PREFIX "unknown/unimp branch\n", ctx->pc); } } static void disasm_auipc(struct disasm_ctx *ctx, tri_t i) { tri_t rd, imm18; parse_u(i, &rd, &imm18); char imm[256]; size_t l = tri_fmt_default(imm, 256, imm18); imm[l] = '\0'; printf(DEFAULT_PREFIX "auipc %s, %s\n", ctx->pc, get_gpr(rd), imm); } static tri_t read1(struct disasm_ctx *ctx, size_t off) { tri_t t = (tri_t)ctx->buf[off]; return tri_mask(t, 9); } static tri_t read3(struct disasm_ctx *ctx, size_t off) { tri_t t0 = read1(ctx, off + 0); tri_t t1 = read1(ctx, off + 1); tri_t t2 = read1(ctx, off + 2); return t0 | tri_sl(t1, 9) | tri_sl(t2, 18); } int disassemble(const char *file) { size_t len = 0; const uint32_t *buf = read_object_file(file, &len); if (!buf) return -1; /* somewhat specific but we know that each tryte is mapped to uin32_t */ struct disasm_ctx ctx = {.buf = buf, .pc = 0, .len = len / sizeof(uint32_t)}; while (ctx.pc < ctx.len) { tri_t i = read3(&ctx, ctx.pc); switch (parse_opcode(i)) { case OPCODE_LUI: disasm_lui(&ctx, i); break; case OPCODE_STORE: disasm_store(&ctx, i); break; case OPCODE_LOAD: disasm_load(&ctx, i); break; //case OPCODE_SYSTEM: disasm_system(&ctx, i); break; case OPCODE_OP_IMM: disasm_op_imm(&ctx, i); break; case OPCODE_OP: disasm_op(&ctx, i); break; case OPCODE_JALR: disasm_jalr(&ctx, i); break; case OPCODE_BRANCH: disasm_branch(&ctx, i); break; case OPCODE_AUIPC: disasm_auipc(&ctx, i); break; default: printf(DEFAULT_PREFIX "unimp/illegal\n", ctx.pc); break; } ctx.pc += 3; } free((void *)buf); return 0; }