use crate::emulator::constants::*;
use crate::emulator::core::OperandType;
use crate::emulator::instructions::functions::{no_floating_point, quad_word_not_supported};
use crate::emulator::{EmulatorInstruction, ForwardComEmulator};
pub fn broad<const VEC_SOURCE: bool>(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
instruction.operand_size = std::cmp::min(
emulator
.operands
.get_u64(operand_indices::INPUT1 + VEC_SOURCE as usize) as usize,
emulator.operands.operand_size(),
);
for i in 0..instruction.operand_size >> instruction.operand_type.log_size() {
emulator.operands.copy(
operand_indices::INPUT2 - VEC_SOURCE as usize,
operand_indices::OUTPUT,
0,
i << instruction.operand_type.log_size(),
1 << instruction.operand_type.log_size(),
);
}
}
pub fn broadcast_max(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
let mut operand = [0u8; 16];
let element_size = 1 << instruction.operand_type.log_size();
instruction.operand_size = emulator.operands.operand_size();
operand[..element_size]
.copy_from_slice(emulator.operands.get(operand_indices::INPUT1, element_size));
emulator
.operands
.set_broadcast(operand_indices::OUTPUT, &operand[..element_size]);
}
pub fn bool_reduce(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
let flags = emulator.operands.get_u8(operand_indices::INPUT2);
let invert_output = ((flags & 0x2) >> 1) ^ (flags & 0x1);
let invert_inputs = (flags & 0x2) >> 1;
let element_size = instruction.operand_type.log_size();
let element_count = instruction.operand_size << element_size;
let value = (0..element_count)
.into_iter()
.map(|index| {
emulator
.operands
.get_offset_u8(operand_indices::INPUT1, index >> element_size)
& 0x1
})
.fold(0, |acc, element| acc & (element ^ invert_inputs))
^ invert_output;
emulator.operands.set_u8(operand_indices::OUTPUT, value);
}
pub fn bool_to_bits(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
let element_index = offset >> instruction.operand_type.log_size();
let byte_index = element_index & !0x7;
let bit_index = element_index & 0x7;
let bit = match instruction.operand_type {
OperandType::I8 => {
(emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset)
& 0x1)
!= 0
}
OperandType::I16 => {
(emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset)
& 0x1)
!= 0
}
OperandType::I32 => {
(emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset)
& 0x1)
!= 0
}
OperandType::I64 => {
(emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset)
& 0x1)
!= 0
}
OperandType::I128 => {
quad_word_not_supported(instruction);
false
}
OperandType::F16 | OperandType::F32 | OperandType::F64 | OperandType::F128 => {
no_floating_point(instruction);
false
}
} as u8;
let value_byte = emulator
.operands
.get_offset_u8(operand_indices::OUTPUT, byte_index)
| (bit << bit_index);
instruction.operand_size = (element_index >> 3) + 1;
emulator
.operands
.set_offset_u8(operand_indices::OUTPUT, byte_index, value_byte);
}
pub fn byte_reverse(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
let bit = (emulator.operands.get_u8(operand_indices::INPUT2) & 0x1) != 0;
match instruction.operand_type {
OperandType::I8 => {
let input = emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset);
let reversed = if bit { input.reverse_bits() } else { input };
emulator
.operands
.set_offset_u8(operand_indices::OUTPUT, offset, reversed);
}
OperandType::I16 | OperandType::F16 => {
let input = emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset);
let reversed = if bit {
input.reverse_bits()
} else {
u16::from_le_bytes(input.to_be_bytes())
};
emulator
.operands
.set_offset_u16(operand_indices::OUTPUT, offset, reversed);
}
OperandType::I32 | OperandType::F32 => {
let input = emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset);
let reversed = if bit {
input.reverse_bits()
} else {
u32::from_le_bytes(input.to_be_bytes())
};
emulator
.operands
.set_offset_u32(operand_indices::OUTPUT, offset, reversed);
}
OperandType::I64 | OperandType::F64 => {
let input = emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset);
let reversed = if bit {
input.reverse_bits()
} else {
u64::from_le_bytes(input.to_be_bytes())
};
emulator
.operands
.set_offset_u64(operand_indices::OUTPUT, offset, reversed);
}
OperandType::I128 | OperandType::F128 => {
quad_word_not_supported(instruction);
}
}
}
pub fn float_to_int(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
let unsigned = (emulator.operands.get_u8(operand_indices::INPUT2) & 0x1) != 0;
match instruction.operand_type {
OperandType::I8 => {
log::debug!("Error: This instruction can't be used on 8 bit integer operands");
instruction.valid = false;
}
OperandType::I16 | OperandType::F16 => {
let value = half::f16::from_bits(
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset),
)
.to_f32();
if unsigned {
emulator
.operands
.set_offset_u16(operand_indices::OUTPUT, offset, value as u16);
} else {
emulator
.operands
.set_offset_i16(operand_indices::OUTPUT, offset, value as i16);
}
}
OperandType::I32 | OperandType::F32 => {
let value = emulator
.operands
.get_offset_f32(operand_indices::INPUT1, offset);
if unsigned {
emulator
.operands
.set_offset_u32(operand_indices::OUTPUT, offset, value as u32);
} else {
emulator
.operands
.set_offset_i32(operand_indices::OUTPUT, offset, value as i32);
}
}
OperandType::I64 | OperandType::F64 => {
let value = emulator
.operands
.get_offset_f64(operand_indices::INPUT1, offset);
if unsigned {
emulator
.operands
.set_offset_u64(operand_indices::OUTPUT, offset, value as u64);
} else {
emulator
.operands
.set_offset_i64(operand_indices::OUTPUT, offset, value as i64);
}
}
OperandType::I128 | OperandType::F128 => {
quad_word_not_supported(instruction);
}
}
}
pub fn int_to_float(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
let unsigned = (emulator.operands.get_u8(operand_indices::INPUT2) & 0x1) != 0;
match instruction.operand_type {
OperandType::I8 => {
log::debug!("Error: This instruction can't be used on 8 bit integer operands");
instruction.valid = false;
}
OperandType::I16 | OperandType::F16 => {
let value = if unsigned {
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset) as f32
} else {
emulator
.operands
.get_offset_i16(operand_indices::INPUT1, offset) as f32
};
emulator.operands.set_offset_u16(
operand_indices::OUTPUT,
offset,
half::f16::from_f32(value).to_bits(),
);
}
OperandType::I32 | OperandType::F32 => {
let value = if unsigned {
emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset) as f32
} else {
emulator
.operands
.get_offset_i32(operand_indices::INPUT1, offset) as f32
};
emulator
.operands
.set_offset_f32(operand_indices::OUTPUT, offset, value);
}
OperandType::I64 | OperandType::F64 => {
let value = if unsigned {
emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset) as f64
} else {
emulator
.operands
.get_offset_i64(operand_indices::INPUT1, offset) as f64
};
emulator
.operands
.set_offset_f64(operand_indices::OUTPUT, offset, value);
}
OperandType::I128 | OperandType::F128 => {
quad_word_not_supported(instruction);
}
}
}
pub fn get_len(emulator: &mut ForwardComEmulator, instruction: &mut EmulatorInstruction, _: usize) {
match instruction.operand_type {
OperandType::I8 => emulator
.operands
.set_u8(operand_indices::OUTPUT, instruction.operand_size as u8),
OperandType::I16 => emulator
.operands
.set_u16(operand_indices::OUTPUT, instruction.operand_size as u16),
OperandType::I32 => emulator
.operands
.set_u32(operand_indices::OUTPUT, instruction.operand_size as u32),
OperandType::I64 => emulator
.operands
.set_u64(operand_indices::OUTPUT, instruction.operand_size as u64),
OperandType::I128 => {
quad_word_not_supported(instruction);
}
OperandType::F16 | OperandType::F32 | OperandType::F64 | OperandType::F128 => {
no_floating_point(instruction);
}
}
}
pub fn get_num(emulator: &mut ForwardComEmulator, instruction: &mut EmulatorInstruction, _: usize) {
emulator.operands.set_u64(
operand_indices::OUTPUT,
(instruction.operand_size >> instruction.operand_type.log_size()) as u64,
);
}
pub fn insert_hi(emulator: &mut ForwardComEmulator, _: &mut EmulatorInstruction, _: usize) {
emulator.operands.set_u64(
operand_indices::OUTPUT,
(emulator.operands.get_u64(operand_indices::INPUT1) & u32::MAX as u64)
| emulator.operands.get_u64(operand_indices::INPUT2),
)
}
pub fn make_sequence(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
instruction.operand_size = std::cmp::min(
(emulator.operands.get_u64(operand_indices::INPUT1) << instruction.operand_type.log_size())
as usize,
emulator.operands.operand_size(),
);
let base = emulator.operands.get_i8(operand_indices::INPUT2);
for i in 0..emulator.operands.operand_size() >> instruction.operand_type.log_size() {
let offset = i << instruction.operand_type.log_size();
match instruction.operand_type {
OperandType::I8 => emulator.operands.set_offset_i8(
operand_indices::OUTPUT,
offset,
base.wrapping_add(i as i8),
),
OperandType::I16 => emulator.operands.set_offset_i16(
operand_indices::OUTPUT,
offset,
(base as i16).wrapping_add(i as i16),
),
OperandType::I32 => emulator.operands.set_offset_i32(
operand_indices::OUTPUT,
offset,
(base as i32).wrapping_add(i as i32),
),
OperandType::I64 => emulator.operands.set_offset_i64(
operand_indices::OUTPUT,
offset,
(base as i64).wrapping_add(i as i64),
),
OperandType::F16 => emulator.operands.set_offset_u16(
operand_indices::OUTPUT,
offset,
(half::f16::from(base) + half::f16::from(i as u8)).to_bits(),
),
OperandType::F32 => emulator.operands.set_offset_f32(
operand_indices::OUTPUT,
offset,
base as f32 + i as f32,
),
OperandType::F64 => emulator.operands.set_offset_f64(
operand_indices::OUTPUT,
offset,
base as f64 + i as f64,
),
OperandType::I128 | OperandType::F128 => {
quad_word_not_supported(instruction);
}
}
}
}
#[inline]
pub fn move_instruction(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
emulator.operands.copy(
operand_indices::INPUT1,
operand_indices::OUTPUT,
offset,
offset,
1 << instruction.operand_type.log_size(),
);
}
#[inline]
pub fn replace(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
emulator.operands.copy(
operand_indices::INPUT2,
operand_indices::OUTPUT,
0,
offset,
1 << instruction.operand_type.log_size(),
);
}
pub fn set_len(emulator: &mut ForwardComEmulator, instruction: &mut EmulatorInstruction, _: usize) {
instruction.operand_size = std::cmp::min(
emulator.operands.get_u64(operand_indices::INPUT2) as usize,
emulator.operands.operand_size(),
);
emulator.operands.copy(
operand_indices::INPUT1,
operand_indices::OUTPUT,
0,
0,
instruction.operand_size,
);
}
pub fn shift_expand(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
let displacement = std::cmp::min(
emulator.operands.get_u64(operand_indices::INPUT2) as usize,
emulator.operands.operand_size() - instruction.operand_size,
);
instruction.operand_size += displacement;
emulator.operands.copy(
operand_indices::INPUT1,
operand_indices::OUTPUT,
0,
displacement,
instruction.operand_size - displacement,
);
}
pub fn shift_reduce(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
let displacement = std::cmp::min(
emulator.operands.get_u64(operand_indices::INPUT2) as usize,
instruction.operand_size,
);
emulator.operands.copy(
operand_indices::INPUT1,
operand_indices::OUTPUT,
displacement,
0,
instruction.operand_size - displacement,
);
instruction.operand_size -= displacement;
}
pub fn shift_up(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
let displacement = std::cmp::min(
(emulator.operands.get_u64(operand_indices::INPUT2) as usize)
<< instruction.operand_type.log_size(),
instruction.operand_size,
);
emulator.operands.copy(
operand_indices::INPUT1,
operand_indices::OUTPUT,
0,
displacement,
instruction.operand_size - displacement,
);
}
pub fn shift_down(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
let displacement = std::cmp::min(
(emulator.operands.get_u64(operand_indices::INPUT2) as usize)
<< instruction.operand_type.log_size(),
instruction.operand_size,
);
emulator.operands.copy(
operand_indices::INPUT1,
operand_indices::OUTPUT,
displacement,
0,
instruction.operand_size - displacement,
);
}
pub fn sign_extend_instruction(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
_: usize,
) {
let result = match instruction.operand_type {
OperandType::I8 => emulator.operands.get_i8(operand_indices::INPUT1) as i64 as u64,
OperandType::I16 => emulator.operands.get_i16(operand_indices::INPUT1) as i64 as u64,
OperandType::I32 => emulator.operands.get_i32(operand_indices::INPUT1) as i64 as u64,
OperandType::I64 => emulator.operands.get_u64(operand_indices::INPUT1),
OperandType::I128 => {
quad_word_not_supported(instruction);
0
}
OperandType::F16 | OperandType::F32 | OperandType::F64 | OperandType::F128 => {
no_floating_point(instruction);
0
}
};
emulator.operands.set_u64(operand_indices::OUTPUT, result);
}