liblisa 0.1.4

A tool for automated discovery and analysis of the ISA of a CPU.
Documentation
use std::fmt::Display;

use itertools::Itertools;

use crate::arch::Arch;
use crate::encoding::bitpattern::{
    FlowOutputLocation, FlowValueLocation, MappingOrBitOrder, Part, PartMapping, PartValue, PART_NAMES,
};
use crate::encoding::dataflows::{AccessKind, Dest, Inputs, Source};
use crate::encoding::Encoding;
use crate::semantics::{Computation, ARG_NAMES};

struct SourceWithParts<'a, A: Arch, C> {
    loc: FlowValueLocation,
    encoding: &'a Encoding<A, C>,
    input: &'a Source<A>,
}

impl<A: Arch, C> Display for SourceWithParts<'_, A, C> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.input {
            Source::Dest(Dest::Reg(reg, size)) => {
                let mut found = false;
                for (part_index, part) in self.encoding.parts.iter().enumerate() {
                    match &part.mapping {
                        PartMapping::Register {
                            locations,
                            mapping: _,
                        } if locations.contains(&self.loc) => {
                            write!(f, "<{}>", PART_NAMES[part_index])?;
                            found = true;
                        },
                        _ => {},
                    }
                }

                if !found {
                    write!(f, "{reg}")?;
                }

                write!(f, "[{}..{}]", size.start_byte, size.end_byte)?;
            },
            Source::Dest(Dest::Mem(mem, size)) => {
                write!(f, "m{mem}")?;

                write!(f, "[{}..{}]", size.start_byte, size.end_byte)?;
            },
            Source::Imm(imm) => {
                write!(f, "<{}>", PART_NAMES[*imm])?;
            },
            other => write!(f, "{other}")?,
        }

        Ok(())
    }
}

struct InputsWithParts<'a, A: Arch, X, C> {
    loc: FlowOutputLocation,
    encoding: &'a Encoding<A, X>,
    inputs: &'a Inputs<A>,
    computation: Option<&'a C>,
}

impl<A: Arch, X, C: Computation> Display for InputsWithParts<'_, A, X, C> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.computation {
            Some(computation) => {
                let names = self
                    .inputs
                    .iter()
                    .enumerate()
                    .map(|(index, input)| {
                        format!(
                            "{}",
                            SourceWithParts {
                                loc: self.loc.input(index).into(),
                                encoding: self.encoding,
                                input
                            }
                        )
                    })
                    .collect::<Vec<_>>();

                write!(f, "{}", computation.display(&names))?;
            },
            None => {
                for (index, input) in self.inputs.iter().enumerate() {
                    if index != 0 {
                        write!(f, ", ")?;
                    }

                    write!(
                        f,
                        "{}",
                        SourceWithParts {
                            loc: self.loc.input(index).into(),
                            encoding: self.encoding,
                            input
                        }
                    )?;
                }
            },
        }

        Ok(())
    }
}

impl<A: Arch, C: Computation> Display for Encoding<A, C> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(
            f,
            "{} [{}]",
            self.bits
                .iter()
                .rev()
                .chunks(8)
                .into_iter()
                .map(|byte| byte.into_iter().map(|s| format!("{s:?}")).join(""))
                .join(" "),
            self.dataflows
                .addresses
                .instr
                .bytes()
                .iter()
                .map(|b| format!("{b:02X}"))
                .join("")
        )?;
        writeln!(
            f,
            "Errors: {}",
            self.errors.iter().map(|&e| if e { "!" } else { "." }).join("")
        )?;

        for (index, access) in self.dataflows.addresses.iter().enumerate() {
            writeln!(
                f,
                "{:10} := {} (size {:?}, align {})",
                format!(
                    "Addr{}(m{})",
                    match access.kind {
                        AccessKind::Executable => "X ",
                        AccessKind::Input => "R ",
                        AccessKind::InputOutput => "RW",
                    },
                    index
                ),
                InputsWithParts {
                    encoding: self,
                    inputs: &access.inputs,
                    loc: FlowOutputLocation::MemoryAccess(index),
                    computation: Some(&access.calculation),
                },
                access.size,
                access.alignment,
            )?;
        }

        for (index, output) in self.dataflows.output_dataflows().enumerate() {
            writeln!(
                f,
                "{:10} := {}",
                format!(
                    "{}",
                    SourceWithParts {
                        encoding: self,
                        input: &Source::Dest(output.target),
                        loc: FlowValueLocation::Output(index)
                    }
                ),
                InputsWithParts {
                    encoding: self,
                    inputs: &output.inputs,
                    loc: FlowOutputLocation::Output(index),
                    computation: output.computation.as_ref(),
                }
            )?;
        }

        writeln!(f, "---")?;

        for (index, part) in self.parts.iter().enumerate() {
            writeln!(f, "<{}>{}", PART_NAMES[index], part)?;
        }

        if !self.write_ordering.is_empty() {
            writeln!(f, "write ordering: {:?}", self.write_ordering)?;
        }

        Ok(())
    }
}

struct DisplayPartMapping<'a, A: Arch> {
    mapping: &'a PartMapping<A>,
    value: u64,
    size: usize,
}

impl<A: Arch> Display for DisplayPartMapping<'_, A> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.mapping {
            PartMapping::Register {
                mapping, ..
            } => {
                for (key, value) in mapping.iter().enumerate() {
                    if key != 0 {
                        write!(f, ", ")?;
                    }

                    write!(f, "{:0pad$b} => ", key, pad = self.size)?;
                    if let Some(reg) = value {
                        if key as u64 == self.value {
                            write!(f, "[ {reg} ]")?;
                        } else {
                            write!(f, "{reg}")?;
                        }
                    } else {
                        write!(f, "-")?;
                    }
                }
            },
            PartMapping::Imm {
                mapping,
                bits,
                ..
            } => {
                write!(f, "immediate = 0x{:X}", self.value)?;

                if let Some(MappingOrBitOrder::Mapping(mapping)) = mapping {
                    if mapping.iter().any(PartValue::is_invalid) {
                        for (value, item) in mapping.iter().enumerate() {
                            if item.is_invalid() {
                                write!(f, " exclude {value}")?;
                            }
                        }
                    }
                }

                if let Some(bits) = bits {
                    write!(f, " [bits: {bits:?}]")?;
                }
            },
            PartMapping::MemoryComputation {
                mapping, ..
            } => {
                for (key, value) in mapping.iter().enumerate() {
                    if key != 0 {
                        write!(f, ", ")?;
                    }

                    write!(f, "{:0pad$b} => ", key, pad = self.size)?;
                    if let Some(computation) = value {
                        if key as u64 == self.value {
                            write!(f, "[ {} ]", computation.display(ARG_NAMES))?;
                        } else {
                            write!(f, "{}", computation.display(ARG_NAMES))?;
                        }
                    } else {
                        write!(f, "-")?;
                    }
                }
            },
        }

        Ok(())
    }
}

impl<A: Arch> Display for Part<A> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "[{} bits]: {}",
            self.size,
            DisplayPartMapping {
                mapping: &self.mapping,
                value: self.value,
                size: self.size,
            }
        )
    }
}