use crate::registers::{get_fp_reg_name, FpRegSize};
use core::fmt::Write;
use disarm64_defn::{defn, InsnClass, InsnFlags, InsnOperandKind, InsnOperandQualifier};
use super::bits::bit_range;
use super::qualifier::{immh_level, qualifier_to_fp_size, scalar_size_qualifier_idx};
pub(crate) fn format_fp_reg(
f: &mut impl Write,
bits: u32,
operand: &defn::InsnOperand,
definition: &defn::Insn,
) -> core::fmt::Result {
let kind = operand.kind;
let reg_no = if let Some(bit_filed) = operand.bit_fields.first() {
bit_range(bits, bit_filed.lsb.into(), bit_filed.width.into())
} else {
return write!(f, ":{kind:?}:");
};
let fp_reg_name = match definition.class {
InsnClass::LDST_IMM9
| InsnClass::LDST_POS
| InsnClass::LDST_REGOFF
| InsnClass::LDST_UNSCALED => {
let size = bit_range(bits, 30, 2);
let opc = bit_range(bits, 22, 2);
if opc == 0 || opc == 1 {
let fp_size = match size {
0b00 => FpRegSize::B8,
0b01 => FpRegSize::H16,
0b10 => FpRegSize::S32,
0b11 => FpRegSize::D64,
_ => unreachable!(),
};
get_fp_reg_name(fp_size, reg_no as usize)
} else if (opc == 0b10 || opc == 0b11) && size == 0 {
get_fp_reg_name(FpRegSize::Q128, reg_no as usize)
} else {
return write!(f, "<undefined>");
}
}
InsnClass::LDSTPAIR_OFF
| InsnClass::LDSTNAPAIR_OFFS
| InsnClass::LDSTPAIR_INDEXED
| InsnClass::LOADLIT => {
let opc = bit_range(bits, 30, 2);
if opc == 0 {
get_fp_reg_name(FpRegSize::S32, reg_no as usize)
} else if opc == 1 {
get_fp_reg_name(FpRegSize::D64, reg_no as usize)
} else if opc == 2 {
get_fp_reg_name(FpRegSize::Q128, reg_no as usize)
} else {
return write!(f, "<undefined>");
}
}
InsnClass::BFLOAT16 => match kind {
InsnOperandKind::Fd => get_fp_reg_name(FpRegSize::H16, reg_no as usize),
InsnOperandKind::Fn => get_fp_reg_name(FpRegSize::S32, reg_no as usize),
_ => {
return write!(f, "<undefined>");
}
},
InsnClass::ASIMDALL => {
let size = bit_range(bits, 22, 2);
let cross_lane = super::bits::bit_set(bits, 15);
if cross_lane {
match size {
0b00 => get_fp_reg_name(FpRegSize::B8, reg_no as usize),
0b01 => get_fp_reg_name(FpRegSize::H16, reg_no as usize),
0b10 => get_fp_reg_name(FpRegSize::S32, reg_no as usize),
0b11 => return write!(f, "<undefined>"),
_ => unreachable!(),
}
} else {
match size {
0b00 => get_fp_reg_name(FpRegSize::H16, reg_no as usize),
0b01 => get_fp_reg_name(FpRegSize::S32, reg_no as usize),
0b10 => get_fp_reg_name(FpRegSize::D64, reg_no as usize),
0b11 => return write!(f, "<undefined>"),
_ => unreachable!(),
}
}
}
_ => {
if definition.flags.contains(InsnFlags::HAS_FPTYPE_FIELD) {
let fp_type = bit_range(bits, 22, 2);
match fp_type {
0b00 => get_fp_reg_name(FpRegSize::S32, reg_no as usize),
0b01 => get_fp_reg_name(FpRegSize::D64, reg_no as usize),
0b10 => "<undefined>",
0b11 => get_fp_reg_name(FpRegSize::H16, reg_no as usize),
_ => unreachable!(),
}
} else if definition
.flags
.contains(InsnFlags::HAS_ADVSIMD_SCALAR_SIZE)
&& operand.qualifiers.len() > 1
{
let size = bit_range(bits, 22, 2) as usize;
let fp_scalar = operand.qualifiers.len() == 2
&& matches!(operand.qualifiers.first(), Some(InsnOperandQualifier::S_S))
&& (definition.mask >> 23) & 1 != 0;
let idx = if fp_scalar {
Some(bit_range(bits, 22, 1) as usize)
} else {
scalar_size_qualifier_idx(operand.qualifiers, size)
};
match idx
.and_then(|i| operand.qualifiers.get(i))
.and_then(qualifier_to_fp_size)
{
Some(fp) => get_fp_reg_name(fp, reg_no as usize),
None => return write!(f, "<undefined>"),
}
} else if operand.qualifiers.len() > 1 {
let level = match immh_level(bits) {
Some(l) => l,
None => return write!(f, "<undefined>"),
};
let base = match operand.qualifiers.first() {
Some(InsnOperandQualifier::S_B) => 0usize,
Some(InsnOperandQualifier::S_H) => 1,
Some(InsnOperandQualifier::S_S) => 2,
Some(InsnOperandQualifier::S_D) => 3,
_ => return write!(f, "<undefined>"),
};
let idx = if operand.qualifiers.len() >= 3 {
level
} else {
level.saturating_sub(base)
};
match operand.qualifiers.get(idx).and_then(qualifier_to_fp_size) {
Some(fp) => get_fp_reg_name(fp, reg_no as usize),
None => return write!(f, "<undefined>"),
}
} else if let Some(qual) = operand.qualifiers.first() {
match qualifier_to_fp_size(qual) {
Some(fp) => get_fp_reg_name(fp, reg_no as usize),
None => return write!(f, "<undefined>"),
}
} else {
return write!(f, ":{kind:?}:");
}
}
};
write!(f, "{fp_reg_name}")
}
pub(crate) fn fp_expand_imm(size: i32, imm8: u32) -> Option<f64> {
let imm8_7 = (imm8 >> 7) & 0x01; let imm8_6_0 = imm8 & 0x7f; let imm8_6 = imm8_6_0 >> 6; let imm8_6_repl4 = (imm8_6 << 3) | (imm8_6 << 2) | (imm8_6 << 1) | imm8_6;
match size {
8 => {
let imm: u64 = ((imm8_7 as u64) << (63-32)) | (((imm8_6 ^ 1) as u64) << (62-32)) | ((imm8_6_repl4 as u64) << (58-32))
| ((imm8_6 as u64) << (57-32))
| ((imm8_6 as u64) << (56-32))
| ((imm8_6 as u64) << (55-32)) | ((imm8_6_0 as u64) << (48-32)); Some(f64::from_bits(imm << 32))
}
4 | 2 => {
let imm = ((imm8_7 as u64) << 31) | (((imm8_6 ^ 1) as u64) << 30) | ((imm8_6_repl4 as u64) << 26) | ((imm8_6_0 as u64) << 19); Some(f32::from_bits(imm as u32) as f64)
}
_ => None,
}
}