charm 0.0.1

ARM assembler & disassembler generated from the ARM exploration tools.
Documentation
//! Various utility functions used to encode/decode instructions.

// -----------------------------------------------------------------------------------------------
// Utility functions.
// -----------------------------------------------------------------------------------------------

/// Returns the log2 of the input value.
fn log2(x: u32) -> u32 {
    let (mut x, mut log) = (x, 0);
    while x != 0 {
        x >>= 1;
        log += 1;
    }
    log - 1
}

/// Returns a mask spanning `width` bits.
fn mask(width: u32) -> u64 {
    (2_u128.pow(width) - 1) as u64
}

fn bitmask_ror(x: u64, r: u32, width: u32) -> u64 {
    let (r, width) = (r as u64, width as u64);
    if r == 0 {
        return x;
    }
    let r = r % width;
    let a = (x >> r) & mask((width - r) as u32);
    let b = x & mask(r as u32);
    return (b << (width - r)) | a;
}
// 1111111111
// ----...... r = 6

fn replicate(pattern: u64, esize: u32, width: u32) -> u64 {
    let mut res = 0u64;
    for i in (0..width).step_by(esize as usize) {
        res |= pattern << i;
    }
    res
}

/// Checks if the input value can be encoded as an ARM modified immediate.
pub fn is_modified_immediate(n: u64) -> bool {
    // Taken from https://codegolf.stackexchange.com/questions/269867/is-it-a-valid-arm-immediate-value
    (0..16).any(|i| ((n << 33 ^ n << 32 ^ (n * 2) ^ n) << (i * 2)) >> 41 < 1)
}

// -----------------------------------------------------------------------------------------------
// ARM functions implementation.
// -----------------------------------------------------------------------------------------------

/// Implementation of the ARM `DecodeBitmasks()` function.
pub fn bitmask_decode<const M: u32>(n: u8, imms: u8, immr: u8) -> (u64, u64) {
    let (n, imms, immr) = (n as u32, imms as u32, immr as u32);
    let val = (n << 6) | (!imms & 0b111111);
    if (val >> 1) == 0 {
        todo!();
    }
    let len = log2(val);
    let levels = 2_u32.pow(len + 1) - 1;
    let s = imms & levels;
    let r = immr & levels;
    let diff = (s.wrapping_sub(r)) & 0b111111;
    let esize = 1 << len;
    let d = diff & mask(len) as u32;
    let welem = mask(s + 1) & mask(esize);
    let telem = mask(d + 1) & mask(esize);
    let tmask = replicate(telem, esize, M);
    let wmask = replicate(bitmask_ror(welem, r, esize), esize, M);
    (wmask, tmask)
}

/// Implementation of the ARM `MoveWidePreferred()` function.
pub fn move_wide_preferred(sf: u32, n: u32, imms: u32, immr: u32) -> bool {
    // See `MoveWidePreferred` at
    // https://mirrors.mit.edu/NetBSD/NetBSD-current/src/sys/arch/aarch64/aarch64/disasm.c

    let mut imm = if sf == 0 {
        bitmask_decode::<32>(n as u8, imms as u8, immr as u8).0
    } else {
        bitmask_decode::<64>(n as u8, imms as u8, immr as u8).0
    };

    if imm & 0xffff_ffff_ffff_0000 == 0
        || imm & 0xffff_ffff_0000_ffff == 0
        || imm & 0xffff_0000_ffff_ffff == 0
        || imm & 0x0000_ffff_ffff_ffff == 0
    {
        return true;
    }

    imm = !imm;
    if sf == 0 {
        imm &= 0xffff_ffff;
    }
    if imm & 0xffff_ffff_ffff_0000 == 0
        || imm & 0xffff_ffff_0000_ffff == 0
        || imm & 0xffff_0000_ffff_ffff == 0
        || imm & 0x0000_ffff_ffff_ffff == 0
    {
        return true;
    }

    return false;
}

/// Implementation of the ARM `BFXPreferred()` function.
pub fn bfx_preferred(sf: u32, uns: u32, imms: u32, immr: u32) -> bool {
    if imms < immr {
        return false;
    }
    if imms == (sf << 5) | 0b11111 {
        return false;
    }
    if immr == 0 {
        if sf == 0 && (imms == 0b000111 || imms == 0b001111) {
            return false;
        }
        if (sf << 1) | uns == 0b10 && (imms == 0b000111 || imms == 0b001111 || imms == 0b011111) {
            return false;
        }
    }
    true
}

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

    #[test]
    fn test_bitmask() {
        assert_eq!(
            bitmask_decode::<64>(0, 0b111100, 0b000000).0,
            0x5555555555555555
        );
        assert_eq!(
            bitmask_decode::<64>(0, 0b111100, 0b000001).0,
            0xaaaaaaaaaaaaaaaa
        );
        assert_eq!(
            bitmask_decode::<64>(0, 0b111000, 0b000000).0,
            0x1111111111111111
        );
        assert_eq!(
            bitmask_decode::<64>(0, 0b111001, 0b000011).0,
            0x6666666666666666
        );
        assert_eq!(
            bitmask_decode::<64>(0, 0b110000, 0b000000).0,
            0x0101010101010101
        );
        assert_eq!(
            bitmask_decode::<64>(0, 0b110000, 0b000000).0,
            0x0101010101010101
        );
    }
}