use crate::registers::FpRegSize;
use crate::registers::SimdRegArrangement;
use disarm64_defn::InsnFlags;
use disarm64_defn::InsnOperandQualifier;
use super::bits::{bit_range, bit_set};
pub(crate) fn qualifier_to_fp_size(qual: &InsnOperandQualifier) -> Option<FpRegSize> {
match qual {
InsnOperandQualifier::S_B => Some(FpRegSize::B8),
InsnOperandQualifier::S_H => Some(FpRegSize::H16),
InsnOperandQualifier::S_S => Some(FpRegSize::S32),
InsnOperandQualifier::S_D => Some(FpRegSize::D64),
InsnOperandQualifier::S_Q => Some(FpRegSize::Q128),
_ => None,
}
}
pub(crate) fn qualifier_element_size(q: &InsnOperandQualifier) -> u8 {
match q {
InsnOperandQualifier::V_8B | InsnOperandQualifier::V_16B => 0,
InsnOperandQualifier::V_2H | InsnOperandQualifier::V_4H | InsnOperandQualifier::V_8H => 1,
InsnOperandQualifier::V_2S | InsnOperandQualifier::V_4S => 2,
InsnOperandQualifier::V_1D | InsnOperandQualifier::V_2D => 3,
_ => 0xFF,
}
}
pub(crate) fn scalar_size_qualifier_idx(
qualifiers: &[InsnOperandQualifier],
size: usize,
) -> Option<usize> {
let idx = if qualifiers.len() >= 3 {
size
} else if qualifiers.len() == 2 {
size.checked_sub(1)?
} else {
0
};
if idx < qualifiers.len() {
Some(idx)
} else {
None
}
}
pub(crate) fn immh_level(bits: u32) -> Option<usize> {
let immh = bit_range(bits, 19, 4);
if immh >= 8 {
Some(3)
} else if immh >= 4 {
Some(2)
} else if immh >= 2 {
Some(1)
} else if immh >= 1 {
Some(0)
} else {
None
}
}
pub(crate) fn asimdins_qualifier_idx(bits: u32) -> Option<usize> {
let imm5 = bit_range(bits, 16, 5);
let q = bit_set(bits, 30) as usize;
if imm5 & 1 != 0 {
Some(q) } else if imm5 & 2 != 0 {
Some(2 + q) } else if imm5 & 4 != 0 {
Some(4 + q) } else if imm5 & 8 != 0 {
if q == 0 {
None } else {
Some(6) }
} else {
None }
}
pub(crate) fn decode_imm5_element(imm5: u32) -> Option<(char, u32)> {
if imm5 & 1 != 0 {
Some(('b', imm5 >> 1))
} else if imm5 & 2 != 0 {
Some(('h', imm5 >> 2))
} else if imm5 & 4 != 0 {
Some(('s', imm5 >> 3))
} else if imm5 & 8 != 0 {
Some(('d', imm5 >> 4))
} else {
None
}
}
pub(crate) fn qualifier_to_simd_reg(qual: &InsnOperandQualifier) -> Option<SimdRegArrangement> {
match qual {
InsnOperandQualifier::V_8B => Some(SimdRegArrangement::Vector8B),
InsnOperandQualifier::V_16B => Some(SimdRegArrangement::Vector16B),
InsnOperandQualifier::V_2H => Some(SimdRegArrangement::Vector2H),
InsnOperandQualifier::V_4H => Some(SimdRegArrangement::Vector4H),
InsnOperandQualifier::V_8H => Some(SimdRegArrangement::Vector8H),
InsnOperandQualifier::V_2S => Some(SimdRegArrangement::Vector2S),
InsnOperandQualifier::V_4S => Some(SimdRegArrangement::Vector4S),
InsnOperandQualifier::V_1D => Some(SimdRegArrangement::Vector1D),
InsnOperandQualifier::V_2D => Some(SimdRegArrangement::Vector2D),
InsnOperandQualifier::V_1Q => Some(SimdRegArrangement::Vector1Q),
_ => None,
}
}
pub(crate) fn qualifier_arrangement(qual: InsnOperandQualifier) -> Option<&'static str> {
match qual {
InsnOperandQualifier::V_8B => Some("8b"),
InsnOperandQualifier::V_16B => Some("16b"),
InsnOperandQualifier::V_4H => Some("4h"),
InsnOperandQualifier::V_8H => Some("8h"),
InsnOperandQualifier::V_2S => Some("2s"),
InsnOperandQualifier::V_4S => Some("4s"),
InsnOperandQualifier::V_1D => Some("1d"),
InsnOperandQualifier::V_2D => Some("2d"),
InsnOperandQualifier::V_1Q => Some("1q"),
_ => None,
}
}
pub(crate) fn resolve_sizeq_arrangement(
qualifiers: &[InsnOperandQualifier],
size: u32,
q: bool,
) -> Option<&'static str> {
let idx = if qualifiers.len() >= 7 {
if size == 3 {
if !q {
return None;
}
6
} else {
size as usize * 2 + q as usize
}
} else if qualifiers.len() >= 4 && qualifiers.first() == Some(&InsnOperandQualifier::V_8B) {
if size == 3 {
if !q {
return None;
}
6.min(qualifiers.len() - 1)
} else {
(size as usize * 2 + q as usize).min(qualifiers.len() - 1)
}
} else {
size as usize * 2 + q as usize
};
qualifiers.get(idx).and_then(|q| qualifier_arrangement(*q))
}
pub(crate) fn resolve_em_qualifier_idx(
qualifiers: &[InsnOperandQualifier],
flags: InsnFlags,
bits: u32,
mask: u32,
) -> usize {
let size = bit_range(bits, 22, 2) as usize;
let q = bit_set(bits, 30) as usize;
let len = qualifiers.len();
if flags.contains(InsnFlags::HAS_SIZEQ_FIELD) {
match len {
4 => {
if size >= 1 {
(size - 1) * 2 + q
} else {
0
}
}
3 => {
let first = qualifiers.first().copied();
if matches!(first, Some(InsnOperandQualifier::S_S)) {
if size & 1 != 0 {
2 } else {
q }
} else {
if size >= 1 {
((size - 1) * 2 + q).min(len - 1)
} else {
0
}
}
}
2 => {
if qualifiers[0] == qualifiers[1] {
q } else {
size.saturating_sub(1)
}
}
_ => 0,
}
} else if flags.contains(InsnFlags::HAS_ADVSIMD_SCALAR_SIZE) {
let fp_scalar = len == 2
&& matches!(qualifiers.first(), Some(InsnOperandQualifier::S_S))
&& (mask >> 23) & 1 != 0;
if fp_scalar {
bit_range(bits, 22, 1) as usize
} else {
scalar_size_qualifier_idx(qualifiers, size).unwrap_or(0)
}
} else {
0
}
}