ruboy_binutils/cli/
dasm.rs

1use std::path::PathBuf;
2
3use clap::{Args, Parser, ValueEnum};
4use ruboy_lib::isa::display::{Case, ImmediateFormat, OperandOrder};
5
6#[derive(Parser, Debug)]
7#[command(author, about, version)]
8pub struct CLIArgs {
9    pub file: PathBuf,
10
11    /// The case used for mnemonics, e.g. "ADD" or "add"
12    #[arg(value_enum, short, long)]
13    pub mnemonic_case: Option<ParsableCase>,
14
15    /// The case used for registers, e.g. "HL" or "hl"
16    #[arg(value_enum, short, long)]
17    pub register_case: Option<ParsableCase>,
18
19    /// Use signs ("HL+") or letters ("HLI") for HLI/HLD
20    #[arg(short = 's', long)]
21    pub hlid_signs: Option<bool>,
22
23    /// Which operand to put first, src or dst
24    #[arg(value_enum, short, long)]
25    pub first_operand: Option<FirstOperand>,
26
27    #[command(flatten)]
28    pub immediate_format: ParsableImmediateFormat,
29}
30
31#[derive(Debug, Clone, Copy, ValueEnum)]
32pub enum ParsableCase {
33    Upper,
34    Lower,
35}
36
37impl From<ParsableCase> for Case {
38    fn from(value: ParsableCase) -> Self {
39        match value {
40            ParsableCase::Upper => Case::Upper,
41            ParsableCase::Lower => Case::Lower,
42        }
43    }
44}
45
46#[derive(Args, Debug, Clone)]
47#[group(id = "immediate_format", multiple = false)]
48pub struct ParsableImmediateFormat {
49    /// Print immediate values in decimal format (e.g. "5" or "11")
50    #[arg(short, long)]
51    pub decimal: bool,
52
53    /// Print immediate values in lowercase hex format, with prefix.
54    /// For example, prefix "0x" results in "0x5" or "0xb"
55    #[arg(short = 'x', long, value_name = "PREFIX", default_missing_value = "$", num_args = 0.., require_equals = true)]
56    pub hex_lowercase: Option<String>,
57
58    /// Print immediate values in lowercase hex format, with prefix.
59    /// For example, prefix "0x" results in "0x5" or "0xB"
60    #[arg(short = 'X', long, value_name = "PREFIX", default_missing_value = "$", num_args = 0.., require_equals = true)]
61    pub hex_uppercase: Option<String>,
62}
63
64impl TryFrom<ParsableImmediateFormat> for ImmediateFormat {
65    type Error = ();
66
67    fn try_from(value: ParsableImmediateFormat) -> Result<Self, Self::Error> {
68        assert!(
69            (value.decimal as u8)
70                + (value.hex_lowercase.is_some() as u8)
71                + (value.hex_uppercase.is_some() as u8)
72                <= 1
73        );
74
75        if value.decimal {
76            Ok(Self::Decimal)
77        } else if let Some(prefix) = value.hex_lowercase {
78            Ok(Self::LowerHex { prefix })
79        } else if let Some(prefix) = value.hex_uppercase {
80            Ok(Self::UpperHex { prefix })
81        } else {
82            Err(())
83        }
84    }
85}
86
87#[derive(Debug, Clone, Copy, ValueEnum)]
88pub enum FirstOperand {
89    Dst,
90    Src,
91}
92
93impl From<FirstOperand> for OperandOrder {
94    fn from(value: FirstOperand) -> Self {
95        match value {
96            FirstOperand::Dst => OperandOrder::DstFirst,
97            FirstOperand::Src => OperandOrder::SrcFirst,
98        }
99    }
100}