disc-v 0.1.3

A RISC-V disassembler
Documentation
#!/usr/bin/env python3
# Copyright (c) 2026 Christopher Swenson
# Licensed under the Apache-2.0 license.
"""Generates src/csr.rs from the CSR lists in a checkout of
https://github.com/riscv/riscv-opcodes (csrs.csv and csrs32.csv).

Usage: scripts/gen-csr-names.py <riscv-opcodes checkout> > src/csr.rs

The output is formatted with rustfmt, which must be installed.
"""
import contextlib, csv, io, os, subprocess, sys

root = sys.argv[1]
names = {}
for f in ('csrs.csv', 'csrs32.csv'):
    for num, name in csv.reader(open(os.path.join(root, f)), skipinitialspace=True):
        num = int(num, 16)
        assert names.setdefault(num, name) == name, (hex(num), name)
rev = subprocess.run(['git', '-C', root, 'rev-parse', 'HEAD'], capture_output=True, text=True).stdout.strip()

# The code is written to a buffer and then formatted with rustfmt (which must
# be installed), so the file passes `cargo fmt --check` like the rest of the
# crate.
code = io.StringIO()
with contextlib.redirect_stdout(code):

    print(f'''// Copyright (c) 2026 Christopher Swenson
    // Licensed under the Apache-2.0 license.

    //! Control and status register names.
    //!
    //! Generated by scripts/gen-csr-names.py from riscv-opcodes {rev}
    //! (Copyright (c) 2022 RISC-V International, BSD-3-Clause).

    /// Returns the name of CSR `csr`, or `None` if it has no standard name.
    pub(crate) fn csr_name(csr: u16) -> Option<&'static str> {{
        Some(match csr {{''')
    for num in sorted(names):
        print(f'        {num:#05x} => "{names[num]}",')
    print('''        _ => return None,
        })
    }

    #[cfg(test)]
    mod tests {
        use super::csr_name;

        #[test]
        fn names_are_unique() {
            let mut names: [Option<&str>; 0x1000] = [None; 0x1000];
            for (csr, slot) in names.iter_mut().enumerate() {
                *slot = csr_name(csr as u16);
            }
            names.sort_unstable();
            for pair in names.windows(2) {
                assert!(
                    pair[0].is_none() || pair[0] != pair[1],
                    "duplicate CSR name {:?}",
                    pair[0]
                );
            }
        }
    }''')

formatted = subprocess.run(['rustfmt', '--edition', '2024'], input=code.getvalue(),
                           capture_output=True, text=True, check=True).stdout
sys.stdout.write(formatted)