charm 0.0.1

ARM assembler & disassembler generated from the ARM exploration tools.
Documentation
//! Default configuration objects and all configuration-related types.

use super::consts::config::ConfigInstructions;
use super::instructions::*;

// -----------------------------------------------------------------------------------------------
// Config
// -----------------------------------------------------------------------------------------------

/// Main configuration object.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub struct Config {
    /// Global configuration.
    ///
    /// This is where the default behavior is set. Values in this structure
    /// act as a fallback if there is no per-instruction settings.
    pub global: ConfigGlobal,
    /// Per-instruction settings that override the default behavior in the global config.
    pub instructions: ConfigInstructions,
}

impl Config {
    /// Creates a new configuration object with default values.
    pub fn new() -> Self {
        Self::default()
    }
}

/// Type that represents the global configuration.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub struct ConfigGlobal {
    /// Global syntax rules.
    pub syntax: FormatSyntaxGlobal,
    /// Global qualifier rules.
    pub qualifier: FormatQualifier,
    /// Global aliases rules.
    pub aliases: FormatAliasGlobal,
}

impl ConfigGlobal {
    /// Creates a new global configuration object.
    pub fn new() -> Self {
        Self::default()
    }
}

/// Type that represents the configuration of an instruction.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct ConfigInstruction<A, E> {
    /// Per-instruction syntax rules.
    pub syntax: FormatSyntax,
    /// Per-instruction qualifier rules.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub qualifier: Option<FormatQualifier>,
    /// Per-instruction aliases rules.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub aliases: Option<FormatAliasInstruction<A>>,
    /// Per-instruction encoding rules.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub encodings: Option<E>,
}

impl<A, E> ConfigInstruction<A, E> {
    pub fn new() -> Self {
        Self::default()
    }
}

impl<A, E> Default for ConfigInstruction<A, E> {
    fn default() -> Self {
        Self {
            syntax: FormatSyntax::default(),
            qualifier: None,
            aliases: None,
            encodings: None,
        }
    }
}

/// Trait to retrieve the syntax and qualifier object.
pub trait HasConfigInstruction {
    fn syntax(&self) -> &FormatSyntax;
    fn qualifier(&self) -> &Option<FormatQualifier>;
}

impl<A, E> HasConfigInstruction for ConfigInstruction<A, E> {
    fn syntax(&self) -> &FormatSyntax {
        &self.syntax
    }

    fn qualifier(&self) -> &Option<FormatQualifier> {
        &self.qualifier
    }
}

// -----------------------------------------------------------------------------------------------
// Aliases
// -----------------------------------------------------------------------------------------------

/// Type that represents the global alias settings.
///
/// It specifies Charm's default behavior when using an alias is possible.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub enum FormatAliasGlobal {
    /// Never use the alias, regardless of any condition.
    Never,
    /// Follows ARM's recommendations.
    #[default]
    Recommended,
    /// Always use the alias, regardless of any condition.
    Always,
}

/// Type that represents the per-instruction alias settings.
///
/// It specifies Charm's behavior for a given encoding when using an alias is possible.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub enum FormatAliasInstruction<A> {
    /// Never use the alias, regardless of any condition.
    Never,
    /// Follows ARM's recommendations.
    #[default]
    Recommended,
    /// If multiple aliases are possible, try to use this alias first.
    Preferred(A),
    /// Always use the alias, regardless of any condition.
    Always(A),
}

// -----------------------------------------------------------------------------------------------
// Qualifier
// -----------------------------------------------------------------------------------------------

/// Type that represents the encoding width qualifier settings.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub enum FormatQualifier {
    /// Always display the encoding width qualifier.
    Always,
    /// Only display the qualifier for wide encodings.
    WideOnly,
    #[default]
    /// Follows ARM's recommendations.
    Recommended,
    /// Never display the encoding width qualifier.
    Never,
}

// -----------------------------------------------------------------------------------------------
// Syntax
// -----------------------------------------------------------------------------------------------

/// Type that represent the integer formatting configuration.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub enum FormatInteger {
    /// Display integers as signed decimal numbers.
    #[default]
    DecimalSigned,
    /// Display integers as unsigned decimal numbers.
    DecimalUnsigned,
    /// Display integers as signed hexdecimal numbers.
    HexadecimalSigned,
    /// Display integers as unsigned hexdecimal numbers.
    HexadecimalUnsigned,
}

/// Type that represent the [`SpecialRegister`](super::operand::SpecialRegister) formatting configuration.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub enum FormatSpecialRegister {
    /// Display special registers in lowercase.
    Lowercase,
    #[default]
    /// Display special registers as they appear in the ARM documentation.
    Recommended,
    /// Display special registers in uppercase.
    Uppercase,
}

/// Type that represents the global syntax settings.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub struct FormatSyntaxGlobal {
    /// Settings for a register that can be omitted when it is *used* as both the source and the
    /// destination.
    /// For example, in `ADCS{<q>} {<Rdn>, }<Rdn>, <Rm>`, this would control whether or not `<Rdn>`
    /// is displayed.
    pub omit_src_dst_same_reg: bool,
    /// Controls if a register that can be omitted when it *can* be used as both the source and the
    /// destination should be displayed.
    /// For example, `ADC{<c>}{<q>} {<Rd>, }<Rn>, <Rm>, RRX`, this would control whether or not
    /// `<Rd>` is displayed if `<Rd> == <Rn>`.
    pub omit_src_dst_diff_reg: bool,
    /// For example, `RFE{IA}{<c>}{<q>} <Rn>{!}`, this would control whether or not `IA` is
    /// displayed.
    pub omit_address_mode: bool,
    /// Controls how positive numbers are displayed.
    pub positive_integer_format: FormatInteger,
    /// Controls how negative numbers are displayed.
    pub negative_integer_format: FormatInteger,
    /// Controls how special registers are displayed.
    pub special_register_format: FormatSpecialRegister,
}

/// Type that represents the per-instruction syntax settings.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub struct FormatSyntax {
    /// Settings for a register that can be omitted when it is *used* as both the source and the
    /// destination.
    /// For example, in `ADCS{<q>} {<Rdn>, }<Rdn>, <Rm>`, this would control whether or not `<Rdn>`
    /// is displayed.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub omit_src_dst_same_reg: Option<bool>,
    /// Controls if a register that can be omitted when it *can* be used as both the source and the
    /// destination should be displayed.
    /// For example, `ADC{<c>}{<q>} {<Rd>, }<Rn>, <Rm>, RRX`, this would control whether or not
    /// `<Rd>` is displayed if `<Rd> == <Rn>`.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub omit_src_dst_diff_reg: Option<bool>,
    /// For example, `RFE{IA}{<c>}{<q>} <Rn>{!}`, this would control whether or not `IA` is
    /// displayed.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub omit_address_mode: Option<bool>,
    /// Controls how positive numbers are displayed.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub positive_integer_format: Option<FormatInteger>,
    /// Controls how negative numbers are displayed.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub negative_integer_format: Option<FormatInteger>,
    /// Controls how special registers are displayed.
    ///
    /// If `None`, defaults to the corresponding global configuration value.
    pub special_register_format: Option<FormatSpecialRegister>,
}

impl FormatSyntax {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn get_omit_src_dst_same_reg(&self, global: &FormatSyntaxGlobal) -> bool {
        if let Some(value) = self.omit_src_dst_same_reg {
            return value;
        }
        global.omit_src_dst_same_reg
    }

    pub fn get_omit_src_dst_diff_reg(&self, global: &FormatSyntaxGlobal) -> bool {
        if let Some(value) = self.omit_src_dst_diff_reg {
            return value;
        }
        global.omit_src_dst_diff_reg
    }

    pub fn get_omit_address_mode(&self, global: &FormatSyntaxGlobal) -> bool {
        if let Some(value) = self.omit_address_mode {
            return value;
        }
        global.omit_address_mode
    }

    pub fn get_positive_integer_format(&self, global: &FormatSyntaxGlobal) -> FormatInteger {
        if let Some(value) = self.positive_integer_format {
            return value;
        }
        global.positive_integer_format
    }

    pub fn get_negative_integer_format(&self, global: &FormatSyntaxGlobal) -> FormatInteger {
        if let Some(value) = self.negative_integer_format {
            return value;
        }
        global.negative_integer_format
    }

    pub fn get_special_register_format(
        &self,
        global: &FormatSyntaxGlobal,
    ) -> FormatSpecialRegister {
        if let Some(value) = self.special_register_format {
            return value;
        }
        global.special_register_format
    }
}

// -----------------------------------------------------------------------------------------------
// LLVM Config
// -----------------------------------------------------------------------------------------------

/// Configuration making Charm behave similarly to LLVM.
pub struct ConfigLLVM {}

impl ConfigLLVM {
    /// Creates a new LLVM configuration.
    pub fn new() -> Config {
        let mut config = Config::default();

        // ---------------------------------------------------------------------------------------
        // Global - Qualifier
        config.global.qualifier = FormatQualifier::Never;

        // ---------------------------------------------------------------------------------------
        // Global - Syntax
        config.global.syntax.omit_address_mode = true;

        // ---------------------------------------------------------------------------------------
        // Per-instruction - Aliases
        config.instructions.adr_a1.aliases =
            Some(FormatAliasInstruction::Always(AdrA1Aliases::AddAdrA1));
        config.instructions.adr_a2.aliases =
            Some(FormatAliasInstruction::Always(AdrA2Aliases::SubAdrA2));
        config.instructions.ldr_i_a1_post.aliases = Some(FormatAliasInstruction::Never);
        config.instructions.mov_i_a2.encodings = Some(MovIA2Encodings::Alt2);
        config.instructions.str_i_a1_pre.aliases = Some(FormatAliasInstruction::Never);

        // ---------------------------------------------------------------------------------------
        // Per-instruction - Syntax
        config.instructions.rfeia_a1_as.syntax.omit_address_mode = Some(false);
        config.instructions.srsia_a1_as.syntax.omit_address_mode = Some(false);
        config.instructions.dmb_a1.syntax.positive_integer_format =
            Some(FormatInteger::HexadecimalUnsigned);
        config.instructions.dsb_a1.syntax.positive_integer_format =
            Some(FormatInteger::HexadecimalUnsigned);
        config.instructions.isb_a1.syntax.positive_integer_format =
            Some(FormatInteger::HexadecimalUnsigned);
        config.instructions.mrs_a1_as.syntax.special_register_format =
            Some(FormatSpecialRegister::Lowercase);

        config
    }
}