riscv/debug.c 21.3 KiB raw
1
#include <stdarg.h>
2
#include <stdio.h>
3
#include <string.h>
4
#include <unistd.h>
5
6
#include "../color.h"
7
#include "../riscv.h"
8
9
#include "debug.h"
10
11
/* Width for opcode field alignment. */
12
#define OP_WIDTH 6
13
14
/* Load instructions based on funct3. */
15
static const char *load_op_names[] = {
16
    "lb",  /* FUNCT3_BYTE */
17
    "lh",  /* FUNCT3_HALF */
18
    "lw",  /* FUNCT3_WORD */
19
    "ld",  /* FUNCT3_DOUBLE (RV64) */
20
    "lbu", /* FUNCT3_BYTE_U */
21
    "lhu", /* FUNCT3_HALF_U */
22
    "lwu", /* FUNCT3_WORD_U */
23
    "?"    /* invalid */
24
};
25
26
/* Store instructions based on funct3. */
27
static const char *store_op_names[] = {
28
    "sb", /* FUNCT3_BYTE */
29
    "sh", /* FUNCT3_HALF */
30
    "sw", /* FUNCT3_WORD */
31
    "sd", /* FUNCT3_DOUBLE (RV64) */
32
    "?",  /* invalid */
33
    "?",  /* invalid */
34
    "?",  /* invalid */
35
    "?"   /* invalid */
36
};
37
38
/* Branch instructions based on funct3. */
39
static const char *branch_op_names[] = {
40
    "beq",  /* FUNCT3_BYTE */
41
    "bne",  /* FUNCT3_HALF */
42
    "?",    /* invalid */
43
    "?",    /* invalid */
44
    "blt",  /* FUNCT3_BYTE_U */
45
    "bge",  /* FUNCT3_HALF_U */
46
    "bltu", /* FUNCT3_OR */
47
    "bgeu"  /* FUNCT3_AND */
48
};
49
50
/* ALU operations with immediates based on funct3. */
51
static const char *alu_imm_op_names[] = {
52
    "addi",  /* FUNCT3_ADD */
53
    "slli",  /* FUNCT3_SLL */
54
    "slti",  /* FUNCT3_SLT */
55
    "sltiu", /* FUNCT3_SLTU */
56
    "xori",  /* FUNCT3_XOR */
57
    "srli",  /* FUNCT3_SRL (may also be SRAI) */
58
    "ori",   /* FUNCT3_OR */
59
    "andi"   /* FUNCT3_AND */
60
};
61
62
/* ALU operations (register-register) based on funct3 and funct7.
63
 * Index as `[funct7][funct3]` */
64
static const char *alu_op_names[4][8] = {
65
    /* FUNCT7_NORMAL (0x00) */
66
    {
67
        "add",  /* FUNCT3_ADD */
68
        "sll",  /* FUNCT3_SLL */
69
        "slt",  /* FUNCT3_SLT */
70
        "sltu", /* FUNCT3_SLTU */
71
        "xor",  /* FUNCT3_XOR */
72
        "srl",  /* FUNCT3_SRL */
73
        "or",   /* FUNCT3_OR */
74
        "and"   /* FUNCT3_AND */
75
    },
76
    /* FUNCT7_SUB (0x20) - Subset of instructions that use this funct7 */
77
    {
78
        "sub", /* FUNCT3_ADD */
79
        "?",   /* invalid */
80
        "?",   /* invalid */
81
        "?",   /* invalid */
82
        "?",   /* invalid */
83
        "sra", /* FUNCT3_SRL */
84
        "?",   /* invalid */
85
        "?"    /* invalid */
86
    },
87
    /* Not used, placeholder for index 2 */
88
    { "?", "?", "?", "?", "?", "?", "?", "?" },
89
    /* FUNCT7_MUL (0x01) - M-extension instructions */
90
    {
91
        "mul",    /* FUNCT3_ADD */
92
        "mulh",   /* FUNCT3_SLL */
93
        "mulhsu", /* FUNCT3_SLT */
94
        "mulhu",  /* FUNCT3_SLTU */
95
        "div",    /* FUNCT3_XOR */
96
        "divu",   /* FUNCT3_SRL */
97
        "rem",    /* FUNCT3_OR */
98
        "remu"    /* FUNCT3_AND */
99
    }
100
};
101
102
/* Print context. */
103
struct ctx {
104
    bool  color;
105
    i32   length;
106
    char *cursor;
107
};
108
109
/* Initialize an empty instruction components structure */
110
static struct instrparts parts(char type) {
111
    struct instrparts components;
112
113
    components.op[0]     = '\0';
114
    components.rd[0]     = '\0';
115
    components.rs1[0]    = '\0';
116
    components.rs2[0]    = '\0';
117
    components.imm[0]    = '\0';
118
    components.off[0]    = '\0';
119
    components.base[0]   = '\0';
120
    components.type      = type;
121
    components.is_pseudo = false;
122
123
    return components;
124
}
125
126
/* Get immediate value from I-type instruction. */
127
i32 get_i_imm(instr_t instr) {
128
    /* Sign-extend 12-bit immediate to 32-bit. */
129
    return sign_extend(instr.i.imm_11_0, 12);
130
}
131
132
/* Get immediate value from S-type instruction. */
133
i32 get_s_imm(instr_t instr) {
134
    /* Sign-extend 12-bit immediate. */
135
    u32 imm = (u32)((instr.s.imm_11_5 << 5) | instr.s.imm_4_0);
136
137
    return sign_extend(imm, 12);
138
}
139
140
/* Get immediate value from B-type instruction. */
141
i32 get_b_imm(instr_t instr) {
142
    u32 imm = (u32)((instr.b.imm_12 << 12) | (instr.b.imm_11 << 11) |
143
                    (instr.b.imm_10_5 << 5) | (instr.b.imm_4_1 << 1));
144
145
    return sign_extend(imm, 13);
146
}
147
148
/* Get immediate value from J-type instruction. */
149
i32 get_j_imm(instr_t instr) {
150
    /* The sign bit is already in bit 20 (imm_20). */
151
    u32 imm = (u32)((instr.j.imm_20 << 20) | (instr.j.imm_19_12 << 12) |
152
                    (instr.j.imm_11 << 11) | (instr.j.imm_10_1 << 1));
153
154
    return sign_extend(imm, 21);
155
}
156
157
/* Map opcodes to decode function and get instruction components. */
158
struct instrparts instr_decode(instr_t instr) {
159
    struct instrparts comp;
160
    i32               imm;
161
    int               funct7_idx;
162
163
    /* Handle `nop` (`addi x0, x0, 0`). */
164
    if (instr.raw == 0x00000013) {
165
        comp = parts('?');
166
        strncpy(comp.op, "nop", MAX_PART_LEN);
167
        comp.is_pseudo = true;
168
        return comp;
169
    }
170
171
    /* Decode based on opcode */
172
    switch (instr.i.opcode) {
173
    case OP_OP:
174
        /* R-type instruction (ADD, SUB, etc.) */
175
        comp = parts('R');
176
177
        /* Compute lookup index for funct7 */
178
        switch (instr.r.funct7) {
179
        case FUNCT7_NORMAL:
180
            funct7_idx = 0;
181
            break;
182
        case FUNCT7_SUB:
183
            funct7_idx = 1;
184
            break;
185
        case FUNCT7_MUL:
186
            funct7_idx = 3;
187
            break;
188
        default:
189
            funct7_idx = 0;
190
            break;
191
        }
192
        strncpy(
193
            comp.op, alu_op_names[funct7_idx][instr.r.funct3], MAX_PART_LEN
194
        );
195
        strncpy(comp.rd, reg_names[instr.r.rd], MAX_PART_LEN);
196
        strncpy(comp.rs1, reg_names[instr.r.rs1], MAX_PART_LEN);
197
        strncpy(comp.rs2, reg_names[instr.r.rs2], MAX_PART_LEN);
198
        break;
199
200
    case OP_IMM:
201
        /* I-type ALU instruction (ADDI, SLTI, etc.) */
202
        comp = parts('I');
203
        imm  = get_i_imm(instr);
204
205
        /* Special case for `mv` pseudo-instruction */
206
        if (instr.i.funct3 == FUNCT3_ADD && instr.i.imm_11_0 == 0 &&
207
            instr.i.rs1 != 0) {
208
            strncpy(comp.op, "mv", MAX_PART_LEN);
209
            strncpy(comp.rd, reg_names[instr.i.rd], MAX_PART_LEN);
210
            strncpy(comp.rs1, reg_names[instr.i.rs1], MAX_PART_LEN);
211
            comp.is_pseudo = true;
212
            return comp;
213
        }
214
215
        /* Special case for `li` pseudo-instruction */
216
        if (instr.i.funct3 == FUNCT3_ADD && instr.i.rs1 == 0) {
217
            strncpy(comp.op, "li", MAX_PART_LEN);
218
            strncpy(comp.rd, reg_names[instr.i.rd], MAX_PART_LEN);
219
            snprintf(comp.imm, MAX_PART_LEN, "%d", imm);
220
            comp.is_pseudo = true;
221
            return comp;
222
        }
223
224
        const char *imm_op = alu_imm_op_names[instr.i.funct3];
225
226
        /* Special case for right shifts (SRAI vs SRLI) */
227
        if (instr.i.funct3 == FUNCT3_SRL && (instr.i.imm_11_0 & 0x400)) {
228
            imm_op = "srai";
229
        }
230
231
        strncpy(comp.op, imm_op, MAX_PART_LEN);
232
        strncpy(comp.rd, reg_names[instr.i.rd], MAX_PART_LEN);
233
        strncpy(comp.rs1, reg_names[instr.i.rs1], MAX_PART_LEN);
234
        snprintf(comp.imm, MAX_PART_LEN, "%d", imm);
235
        break;
236
237
    case OP_IMM_32: {
238
        /* RV64I: 32-bit immediate operations (ADDIW, SLLIW, SRLIW, SRAIW) */
239
        comp = parts('I');
240
        imm  = get_i_imm(instr);
241
242
        /* Special case: sext.w pseudo-instruction (addiw rd, rs1, 0) */
243
        if (instr.i.funct3 == FUNCT3_ADD && instr.i.imm_11_0 == 0) {
244
            strncpy(comp.op, "sext.w", MAX_PART_LEN);
245
            strncpy(comp.rd, reg_names[instr.i.rd], MAX_PART_LEN);
246
            strncpy(comp.rs1, reg_names[instr.i.rs1], MAX_PART_LEN);
247
            comp.is_pseudo = true;
248
            return comp;
249
        }
250
251
        const char *w_op;
252
        switch (instr.i.funct3) {
253
        case FUNCT3_ADD:
254
            w_op = "addiw";
255
            break;
256
        case FUNCT3_SLL:
257
            w_op = "slliw";
258
            break;
259
        case FUNCT3_SRL:
260
            w_op = (instr.i.imm_11_0 & 0x400) ? "sraiw" : "srliw";
261
            break;
262
        default:
263
            w_op = "?w";
264
            break;
265
        }
266
267
        strncpy(comp.op, w_op, MAX_PART_LEN);
268
        strncpy(comp.rd, reg_names[instr.i.rd], MAX_PART_LEN);
269
        strncpy(comp.rs1, reg_names[instr.i.rs1], MAX_PART_LEN);
270
        snprintf(comp.imm, MAX_PART_LEN, "%d", imm);
271
        break;
272
    }
273
274
    case OP_OP_32: {
275
        /* RV64I: 32-bit register-register operations */
276
        comp = parts('R');
277
        const char *w_rop;
278
        switch (instr.r.funct7) {
279
        case FUNCT7_NORMAL:
280
            switch (instr.r.funct3) {
281
            case FUNCT3_ADD:
282
                w_rop = "addw";
283
                break;
284
            case FUNCT3_SLL:
285
                w_rop = "sllw";
286
                break;
287
            case FUNCT3_SRL:
288
                w_rop = "srlw";
289
                break;
290
            default:
291
                w_rop = "?w";
292
                break;
293
            }
294
            break;
295
        case FUNCT7_SUB:
296
            switch (instr.r.funct3) {
297
            case FUNCT3_ADD:
298
                w_rop = "subw";
299
                break;
300
            case FUNCT3_SRL:
301
                w_rop = "sraw";
302
                break;
303
            default:
304
                w_rop = "?w";
305
                break;
306
            }
307
            break;
308
        case FUNCT7_MUL:
309
            switch (instr.r.funct3) {
310
            case FUNCT3_ADD:
311
                w_rop = "mulw";
312
                break;
313
            case FUNCT3_XOR:
314
                w_rop = "divw";
315
                break;
316
            case FUNCT3_SRL:
317
                w_rop = "divuw";
318
                break;
319
            case FUNCT3_OR:
320
                w_rop = "remw";
321
                break;
322
            case FUNCT3_AND:
323
                w_rop = "remuw";
324
                break;
325
            default:
326
                w_rop = "?w";
327
                break;
328
            }
329
            break;
330
        default:
331
            w_rop = "?w";
332
            break;
333
        }
334
        strncpy(comp.op, w_rop, MAX_PART_LEN);
335
        strncpy(comp.rd, reg_names[instr.r.rd], MAX_PART_LEN);
336
        strncpy(comp.rs1, reg_names[instr.r.rs1], MAX_PART_LEN);
337
        strncpy(comp.rs2, reg_names[instr.r.rs2], MAX_PART_LEN);
338
        break;
339
    }
340
341
    case OP_LOAD:
342
        /* I-type Load instruction (LW, LH, etc.) */
343
        comp = parts('I');
344
        imm  = get_i_imm(instr);
345
346
        strncpy(comp.op, load_op_names[instr.i.funct3], MAX_PART_LEN);
347
        strncpy(comp.rd, reg_names[instr.i.rd], MAX_PART_LEN);
348
        snprintf(comp.off, MAX_PART_LEN, "%d", imm);
349
        strncpy(comp.base, reg_names[instr.i.rs1], MAX_PART_LEN);
350
        break;
351
352
    case OP_JALR:
353
        /* I-type JALR instruction */
354
        comp = parts('I');
355
        imm  = get_i_imm(instr);
356
357
        /* Special case for `ret` pseudo-instruction */
358
        if (instr.i.rd == 0 && instr.i.rs1 == 1 && instr.i.imm_11_0 == 0) {
359
            strncpy(comp.op, "ret", MAX_PART_LEN);
360
            comp.is_pseudo = true;
361
            return comp;
362
        }
363
364
        strncpy(comp.op, "jalr", MAX_PART_LEN);
365
        strncpy(comp.rd, reg_names[instr.i.rd], MAX_PART_LEN);
366
        snprintf(comp.off, MAX_PART_LEN, "%d", imm);
367
        strncpy(comp.base, reg_names[instr.i.rs1], MAX_PART_LEN);
368
        break;
369
370
    case OP_STORE:
371
        /* S-type instruction (SW, SH, etc.) */
372
        comp = parts('S');
373
        imm  = get_s_imm(instr);
374
375
        strncpy(comp.op, store_op_names[instr.s.funct3], MAX_PART_LEN);
376
        strncpy(comp.rs2, reg_names[instr.s.rs2], MAX_PART_LEN);
377
        snprintf(comp.off, MAX_PART_LEN, "%d", imm);
378
        strncpy(comp.base, reg_names[instr.s.rs1], MAX_PART_LEN);
379
        break;
380
381
    case OP_BRANCH:
382
        /* B-type instruction (BEQ, BNE, etc.) */
383
        comp = parts('B');
384
        imm  = get_b_imm(instr);
385
386
        strncpy(comp.op, branch_op_names[instr.b.funct3], MAX_PART_LEN);
387
        strncpy(comp.rs1, reg_names[instr.b.rs1], MAX_PART_LEN);
388
        strncpy(comp.rs2, reg_names[instr.b.rs2], MAX_PART_LEN);
389
        snprintf(comp.imm, MAX_PART_LEN, "%d", imm);
390
        break;
391
392
    case OP_LUI:
393
    case OP_AUIPC:
394
        /* U-type instruction (LUI, AUIPC) */
395
        comp = parts('U');
396
397
        /* Choose operation name based on opcode */
398
        strncpy(
399
            comp.op, (instr.u.opcode == OP_LUI) ? "lui" : "auipc", MAX_PART_LEN
400
        );
401
        strncpy(comp.rd, reg_names[instr.u.rd], MAX_PART_LEN);
402
        snprintf(comp.imm, MAX_PART_LEN, "0x%x", instr.u.imm_31_12);
403
        break;
404
405
    case OP_JAL:
406
        /* J-type instruction (JAL) */
407
        comp = parts('J');
408
        imm  = get_j_imm(instr);
409
410
        /* J pseudo-instruction (JAL with rd=x0) */
411
        if (instr.j.rd == 0) {
412
            strncpy(comp.op, "j", MAX_PART_LEN);
413
            snprintf(comp.imm, MAX_PART_LEN, "%d", imm);
414
            comp.is_pseudo = true;
415
        } else {
416
            strncpy(comp.op, "jal", MAX_PART_LEN);
417
            strncpy(comp.rd, reg_names[instr.j.rd], MAX_PART_LEN);
418
            snprintf(comp.imm, MAX_PART_LEN, "%d", imm);
419
        }
420
        break;
421
422
    case OP_SYSTEM:
423
        /* System instructions */
424
        if (instr.i.imm_11_0 == 0) {
425
            /* ECALL instruction */
426
            comp = parts('I');
427
            strncpy(comp.op, "ecall", MAX_PART_LEN);
428
            return comp;
429
        } else if (instr.i.imm_11_0 == 1) {
430
            /* EBREAK instruction */
431
            comp = parts('I');
432
            strncpy(comp.op, "ebreak", MAX_PART_LEN);
433
            return comp;
434
        }
435
        /* Fall through for unknown system instructions */
436
437
        /* fall through */
438
    case OP_LOAD_FP:
439
        /* F Extension - Floating-point load instruction */
440
        comp = parts('I');
441
        imm  = get_i_imm(instr);
442
443
        if (instr.i.funct3 == FUNCT3_WORD_FP) {
444
            strncpy(comp.op, "flw", MAX_PART_LEN);
445
        } else {
446
            strncpy(
447
                comp.op, "flw", MAX_PART_LEN
448
            ); /* Only single precision supported */
449
        }
450
        strncpy(comp.rd, reg_names[instr.i.rd], MAX_PART_LEN);
451
        snprintf(comp.off, MAX_PART_LEN, "%d", imm);
452
        strncpy(comp.base, reg_names[instr.i.rs1], MAX_PART_LEN);
453
        break;
454
455
    case OP_STORE_FP:
456
        /* F Extension - Floating-point store instruction */
457
        comp = parts('S');
458
        imm  = get_s_imm(instr);
459
460
        if (instr.s.funct3 == FUNCT3_WORD_FP) {
461
            strncpy(comp.op, "fsw", MAX_PART_LEN);
462
        } else {
463
            strncpy(
464
                comp.op, "fsw", MAX_PART_LEN
465
            ); /* Only single precision supported */
466
        }
467
        strncpy(comp.rs2, reg_names[instr.s.rs2], MAX_PART_LEN);
468
        snprintf(comp.off, MAX_PART_LEN, "%d", imm);
469
        strncpy(comp.base, reg_names[instr.s.rs1], MAX_PART_LEN);
470
        break;
471
472
    case OP_OP_FP:
473
        /* F Extension - Floating-point operation instruction */
474
        comp = parts('R');
475
476
        switch (instr.r.funct7) {
477
        case FUNCT7_FADD_S:
478
            strncpy(comp.op, "fadd.s", MAX_PART_LEN);
479
            break;
480
        case FUNCT7_FSUB_S:
481
            strncpy(comp.op, "fsub.s", MAX_PART_LEN);
482
            break;
483
        case FUNCT7_FMUL_S:
484
            strncpy(comp.op, "fmul.s", MAX_PART_LEN);
485
            break;
486
        case FUNCT7_FDIV_S:
487
            strncpy(comp.op, "fdiv.s", MAX_PART_LEN);
488
            break;
489
        case FUNCT7_FEQ_S:
490
            /* Comparison instructions use different funct3 values */
491
            switch (instr.r.funct3) {
492
            case FUNCT3_FEQ:
493
                strncpy(comp.op, "feq.s", MAX_PART_LEN);
494
                break;
495
            case FUNCT3_FLT:
496
                strncpy(comp.op, "flt.s", MAX_PART_LEN);
497
                break;
498
            case FUNCT3_FLE:
499
                strncpy(comp.op, "fle.s", MAX_PART_LEN);
500
                break;
501
            default:
502
                strncpy(comp.op, "fcmp.s", MAX_PART_LEN);
503
                break;
504
            }
505
            break;
506
        default:
507
            strncpy(comp.op, "fop.s", MAX_PART_LEN);
508
            break;
509
        }
510
        strncpy(comp.rd, reg_names[instr.r.rd], MAX_PART_LEN);
511
        strncpy(comp.rs1, reg_names[instr.r.rs1], MAX_PART_LEN);
512
        strncpy(comp.rs2, reg_names[instr.r.rs2], MAX_PART_LEN);
513
        break;
514
515
    default:
516
        /* Unknown opcode */
517
        comp = parts('?');
518
        snprintf(comp.op, MAX_PART_LEN, "unknown (%d)", instr.i.opcode);
519
        break;
520
    }
521
    return comp;
522
}
523
524
/* Helper function to format colored text with automatic reset.
525
 * When color is COLOR_NORMAL, outputs plain text with no color codes */
526
static void put(struct ctx *ctx, const char *color, const char *str) {
527
    if (color && ctx->color) {
528
        ctx->cursor += sprintf(ctx->cursor, "%s", color);
529
    }
530
    i32 len = sprintf(ctx->cursor, "%s", str);
531
532
    ctx->cursor += len;
533
    ctx->length += len;
534
535
    if (color && ctx->color) {
536
        ctx->cursor += sprintf(ctx->cursor, "%s", COLOR_RESET);
537
    }
538
}
539
540
/* Format instruction components to a string.
541
 * If use_color is true, the output will include ANSI color codes. */
542
static void format_instr(const struct instrparts *i, struct ctx *ctx) {
543
    /* Define colors based on whether color is enabled */
544
    const char *opcolor  = COLOR_BOLD;
545
    const char *rdcolor  = COLOR_GREEN;
546
    const char *rscolor  = COLOR_GREEN;
547
    const char *immcolor = COLOR_BLUE;
548
    const char *nocolor  = COLOR_NORMAL;
549
550
    put(ctx, opcolor, i->op);
551
552
    /* Check if this is an op without operands. */
553
    if (i->is_pseudo &&
554
        (strcmp(i->op, "ret") == 0 || strcmp(i->op, "nop") == 0)) {
555
        return;
556
    }
557
    if (strcmp(i->op, "ebreak") == 0 || strcmp(i->op, "ecall") == 0)
558
        return;
559
560
    /* Padding for ops with operands */
561
    for (usize len = strlen(i->op); len <= OP_WIDTH; len++)
562
        put(ctx, nocolor, " ");
563
564
    if (i->is_pseudo) {
565
        if (strcmp(i->op, "j") == 0) {
566
            put(ctx, nocolor, " ");
567
            put(ctx, immcolor, i->imm);
568
            return;
569
        } else if (strcmp(i->op, "mv") == 0) {
570
            put(ctx, nocolor, " ");
571
            put(ctx, rdcolor, i->rd);
572
            put(ctx, nocolor, ", ");
573
            put(ctx, rscolor, i->rs1);
574
            return;
575
        } else if (strcmp(i->op, "li") == 0) {
576
            put(ctx, nocolor, " ");
577
            put(ctx, rdcolor, i->rd);
578
            put(ctx, nocolor, ", ");
579
            put(ctx, immcolor, i->imm);
580
            return;
581
        }
582
    }
583
584
    switch (i->type) {
585
    case 'R':
586
        /* R-type: op rd, rs1, rs2. */
587
        put(ctx, nocolor, " ");
588
        put(ctx, rdcolor, i->rd);
589
        put(ctx, nocolor, ", ");
590
        put(ctx, rscolor, i->rs1);
591
        put(ctx, nocolor, ", ");
592
        put(ctx, rscolor, i->rs2);
593
        break;
594
595
    case 'I':
596
        if (i->base[0] != '\0') {
597
            /* I-type (load/jalr): op rd, offset(rs1). */
598
            put(ctx, nocolor, " ");
599
            put(ctx, rdcolor, i->rd);
600
            put(ctx, nocolor, ", ");
601
            put(ctx, immcolor, i->off);
602
            put(ctx, nocolor, "(");
603
            put(ctx, rscolor, i->base);
604
            put(ctx, nocolor, ")");
605
        } else {
606
            /* I-type (immediate): op rd, rs1, imm. */
607
            put(ctx, nocolor, " ");
608
            put(ctx, rdcolor, i->rd);
609
            put(ctx, nocolor, ", ");
610
            put(ctx, rscolor, i->rs1);
611
            put(ctx, nocolor, ", ");
612
            put(ctx, immcolor, i->imm);
613
        }
614
        break;
615
616
    case 'S':
617
        /* S-type: op rs2, offset(rs1). */
618
        put(ctx, nocolor, " ");
619
        put(ctx, rscolor, i->rs2);
620
        put(ctx, nocolor, ", ");
621
        put(ctx, immcolor, i->off);
622
        put(ctx, nocolor, "(");
623
        put(ctx, rscolor, i->base);
624
        put(ctx, nocolor, ")");
625
        break;
626
627
    case 'B':
628
        /* B-type: op rs1, rs2, imm. */
629
        put(ctx, nocolor, " ");
630
        put(ctx, rscolor, i->rs1);
631
        put(ctx, nocolor, ", ");
632
        put(ctx, rscolor, i->rs2);
633
        put(ctx, nocolor, ", ");
634
        put(ctx, immcolor, i->imm);
635
        break;
636
637
    case 'U':
638
        /* U-type: op rd, imm. */
639
        put(ctx, nocolor, " ");
640
        put(ctx, rdcolor, i->rd);
641
        put(ctx, nocolor, ", ");
642
        put(ctx, immcolor, i->imm);
643
        break;
644
645
    case 'J':
646
        /* J-type: op rd, imm. */
647
        put(ctx, nocolor, " ");
648
        if (i->rd[0] != '\0') {
649
            put(ctx, rdcolor, i->rd);
650
            put(ctx, nocolor, ", ");
651
            put(ctx, immcolor, i->imm);
652
        } else {
653
            put(ctx, immcolor, i->imm);
654
        }
655
        break;
656
657
    default:
658
        /* Unknown type or invalid opcode. */
659
        break;
660
    }
661
}
662
663
i32 sprint_instr(instr_t instr, char *buf, bool color) {
664
    struct ctx ctx = (struct ctx){ .color = color, .length = 0, .cursor = buf };
665
    struct instrparts parts = instr_decode(instr);
666
667
    format_instr(&parts, &ctx);
668
669
    return ctx.length;
670
}
671
672
void print_instr(
673
    instr_t instr, FILE *stream, const char *comment, bool color, i32 indent
674
) {
675
    char buf[MAX_INSTR_STR_LEN] = { 0 };
676
    i32  n                      = sprint_instr(instr, buf, color);
677
678
    color = color && isatty(stream->_fileno);
679
680
    /* Indent line */
681
    fprintf(stream, "%*s", indent, " ");
682
683
    if (comment) {
684
        fprintf(stream, "%s", buf);
685
        fprintf(stream, "%-*s", INSTR_STR_LEN - n, "");
686
687
        if (color) {
688
            fputs(COLOR_GREY, stream);
689
        }
690
        fprintf(stream, "# %s\n", comment);
691
692
        if (color) {
693
            fputs(COLOR_RESET, stream);
694
        }
695
    } else {
696
        fprintf(stream, "%s\n", buf);
697
    }
698
}
699
700
void print_instrs(
701
    instr_t *instrs, usize count, FILE *stream, const char *comment
702
) {
703
    bool color = (bool)isatty(stream->_fileno);
704
705
    for (usize i = 0; i < count; i++) {
706
        char  buf[MAX_INSTR_STR_LEN];
707
        usize addr = i * INSTR_SIZE;
708
        sprint_instr(instrs[i], buf, color);
709
710
        if (comment) {
711
            fprintf(
712
                stream,
713
                "%04lx: %08x  %s # %s\n",
714
                addr,
715
                instrs[i].raw,
716
                buf,
717
                comment
718
            );
719
        } else {
720
            fprintf(stream, "%04lx: %08x  %s\n", addr, instrs[i].raw, buf);
721
        }
722
    }
723
}