Skip to main content

disc_v/
format.rs

1// Copyright (c) 2026 Christopher Swenson
2// Licensed under the Apache-2.0 license.
3//
4// Derived from riscv-disassembler, Copyright (c) 2016-2017 Michael Clark
5// and Copyright (c) 2017-2018 SiFive, Inc., under the MIT license; see NOTICE.
6
7//! Text output for decoded instructions, in the style of GNU objdump.
8
9use core::fmt::{self, Display, Write};
10
11use crate::csr::csr_name;
12use crate::instruction::Instruction;
13use crate::reg::{FP_NAMES, INT_NAMES};
14
15impl Display for Instruction {
16    /// Formats the instruction as objdump does, with a single space between
17    /// the mnemonic and the operands: `lw a0,8(sp)`.
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        f.write_str(self.op.name)?;
20        match (self.aq, self.rl) {
21            (true, true) => f.write_str(".aqrl")?,
22            (true, false) => f.write_str(".aq")?,
23            (false, true) => f.write_str(".rl")?,
24            (false, false) => {}
25        }
26        if !self.op.format.is_empty() {
27            write!(f, " {}", self.operands())?;
28        }
29        Ok(())
30    }
31}
32
33/// The operands of an instruction, formatted as objdump does: `a0,8(sp)`.
34///
35/// Create one with [`Instruction::operands`].
36#[derive(Clone, Copy, Debug)]
37pub struct Operands<'a>(pub(crate) &'a Instruction);
38
39impl Display for Operands<'_> {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        let ins = self.0;
42        for c in ins.op.format.chars() {
43            write_part(f, ins, c)?;
44        }
45        Ok(())
46    }
47}
48
49/// Writes the output for format character `c`; see
50/// [`fmt`](crate::opcodes::fmt).
51fn write_part(out: &mut fmt::Formatter<'_>, ins: &Instruction, c: char) -> fmt::Result {
52    match c {
53        '(' | ',' | ' ' | ')' => out.write_char(c),
54        '0' => out.write_str(INT_NAMES[ins.rd as usize]),
55        '1' => out.write_str(INT_NAMES[ins.rs1 as usize]),
56        '2' => out.write_str(INT_NAMES[ins.rs2 as usize]),
57        '3' => out.write_str(FP_NAMES[ins.rd as usize]),
58        '4' => out.write_str(FP_NAMES[ins.rs1 as usize]),
59        '5' => out.write_str(FP_NAMES[ins.rs2 as usize]),
60        '6' => out.write_str(FP_NAMES[ins.rs3 as usize]),
61        '7' => write!(out, "{}", ins.rs1),
62        'i' => write!(out, "{}", ins.imm),
63        '>' => write!(out, "{:#x}", ins.imm),
64        'u' => write!(out, "{:#x}", (ins.imm as u32 >> 12) & 0xfffff),
65        'o' => write!(out, "{:#x}", ins.target()),
66        'c' => match csr_name(ins.imm as u16 & 0xfff) {
67            Some(name) => out.write_str(name),
68            None => write!(out, "{:#x}", ins.imm & 0xfff),
69        },
70        'r' if ins.rm == 7 => Ok(()),
71        'R' if ins.rm == 0 => Ok(()),
72        'r' | 'R' => {
73            let names = [
74                "rne", "rtz", "rdn", "rup", "rmm", "unknown", "unknown", "dyn",
75            ];
76            write!(out, ",{}", names[ins.rm as usize])
77        }
78        'l' => write!(out, "{}", ins.len),
79        'x' => {
80            // As objdump does, padded to a whole number of 16-bit parcels.
81            let parcels = (64 - ins.inst.leading_zeros()).div_ceil(16).max(1) as usize;
82            write!(out, "0x{:01$x}", ins.inst, parcels * 4)
83        }
84        'p' => write_fence_set(out, ins.pred),
85        's' => write_fence_set(out, ins.succ),
86        _ => unreachable!("unknown format character {c:?}"),
87    }
88}
89
90/// Writes the device input, device output, memory read and memory write bits
91/// of a fence predecessor or successor set.
92fn write_fence_set(out: &mut fmt::Formatter<'_>, set: u8) -> fmt::Result {
93    if set == 0 {
94        return out.write_str("unknown");
95    }
96    for (bit, name) in [(8, 'i'), (4, 'o'), (2, 'r'), (1, 'w')] {
97        if set & bit != 0 {
98            out.write_char(name)?;
99        }
100    }
101    Ok(())
102}