charm 0.0.1

ARM assembler & disassembler generated from the ARM exploration tools.
Documentation
//! Instructions decoder.

use super::config::*;
use super::instruction::*;
use super::operand::*;
use crate::error::Result;
use std::collections::HashSet;

pub(crate) mod decoder;

/// Main decoder object.
#[derive(Clone, Debug)]
pub struct Decoder<'a> {
    pub(crate) data: &'a [u8],
    pub(crate) config: Config,
    pub(crate) it_block: Option<ItBlock>,
    pub(crate) block_decoding: bool,
    pub(crate) pc: u32,
    pub(crate) labels: HashSet<u32>,
}

impl<'a> Decoder<'a> {
    /// Creates a new decoder.
    pub fn new(data: &'a [u8], config: Config) -> Self {
        Self {
            data,
            it_block: None,
            config,
            block_decoding: false,
            pc: 0,
            labels: HashSet::new(),
        }
    }

    /// Decodes one instruction.
    pub fn decode(&mut self) -> Result<Instruction> {
        let mut instruction = decoder::decode(self)?;
        self.check_condition(&mut instruction)?;
        instruction.pc = self.pc;
        self.pc += instruction.size as u32;
        Ok(instruction)
    }

    /// Decodes an instruction block.
    pub fn decode_block(&mut self, pc: u32) -> Result<InstructionBlock> {
        self.pc = pc;
        self.labels.clear();
        self.block_decoding = true;
        let mut block = InstructionBlock::new(pc);
        while !self.data.is_empty() {
            block.push_instruction(self.decode()?);
        }
        self.block_decoding = false;

        let instructions = block
            .instructions
            .into_iter()
            .fold(vec![], |mut v, element| match element {
                InstructionBlockElement::Label(_) => unreachable!(),
                InstructionBlockElement::Instruction(i) => {
                    if self.labels.contains(&i.pc) {
                        v.push(InstructionBlockElement::Label(i.pc as u64));
                    }
                    v.push(element);
                    v
                }
            });

        Ok(InstructionBlock::with_instructions(pc, instructions))
    }

    /// Evaluates the condition and decodes the instruction based on the current decoder's state.
    pub fn check_condition(&mut self, instruction: &mut Instruction) -> Result<()> {
        match instruction.condition() {
            ConditionalInstruction::ItBlock(cond, it_cond_1, it_cond_2, it_cond_3) => {
                if self.it_block.is_some() {
                    todo!()
                }
                let states = [ItCondition::If, it_cond_1, it_cond_2, it_cond_3];
                let count = states.iter().fold(0, |acc, c| {
                    acc + if *c == ItCondition::None { 0 } else { 1 }
                });
                self.it_block = Some(ItBlock {
                    condition: cond,
                    states,
                    index: 0,
                    count,
                });
            }
            ConditionalInstruction::Condition(index, last_only, _settable) => {
                if let Some(it_block) = &mut self.it_block {
                    match it_block.states[it_block.index] {
                        ItCondition::None => todo!(),
                        ItCondition::If => {
                            if last_only {
                                if it_block.index != it_block.states.len() - 1
                                    && !it_block.states[it_block.index + 1].is_none()
                                {
                                    todo!()
                                }
                            }
                            let op = Operand::MnemonicCondition(it_block.condition.into());
                            instruction.set_op(index, op)?;
                        }
                        ItCondition::Else => {
                            if last_only {
                                if it_block.index != it_block.states.len() - 1
                                    && !it_block.states[it_block.index + 1].is_none()
                                {
                                    todo!()
                                }
                            }
                            let op =
                                Operand::MnemonicCondition(it_block.condition.opposite().into());
                            instruction.set_op(index, op)?;
                        }
                    }
                    it_block.index += 1;
                }
            }
            ConditionalInstruction::None => {
                if let Some(it_block) = &mut self.it_block {
                    it_block.index += 1;
                } else {
                    return Ok(());
                }
            }
        }
        // Once we reached the end of the IT block conditions list, we can remove it.
        let _ = self.it_block.take_if(|b| b.index == b.count);
        Ok(())
    }
}