use crate::emulator::constants::operand_indices;
use crate::emulator::core::OperandType;
use crate::emulator::instructions::functions::{no_floating_point, quad_word_not_supported};
use crate::emulator::{EmulatorInstruction, ForwardComEmulator};
pub fn and(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
match instruction.operand_type {
OperandType::I8 => emulator.operands.set_offset_u8(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset)
& emulator
.operands
.get_offset_u8(operand_indices::INPUT2, offset),
),
OperandType::I16 | OperandType::F16 => emulator.operands.set_offset_u16(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset)
& emulator
.operands
.get_offset_u16(operand_indices::INPUT2, offset),
),
OperandType::I32 | OperandType::F32 => emulator.operands.set_offset_u32(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset)
& emulator
.operands
.get_offset_u32(operand_indices::INPUT2, offset),
),
OperandType::I64 | OperandType::F64 => emulator.operands.set_offset_u64(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset)
& emulator
.operands
.get_offset_u64(operand_indices::INPUT2, offset),
),
OperandType::I128 | OperandType::F128 => {
quad_word_not_supported(instruction);
}
}
}
pub fn or(emulator: &mut ForwardComEmulator, instruction: &mut EmulatorInstruction, offset: usize) {
match instruction.operand_type {
OperandType::I8 => emulator.operands.set_offset_u8(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset)
| emulator
.operands
.get_offset_u8(operand_indices::INPUT2, offset),
),
OperandType::I16 | OperandType::F16 => emulator.operands.set_offset_u16(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset)
| emulator
.operands
.get_offset_u16(operand_indices::INPUT2, offset),
),
OperandType::I32 | OperandType::F32 => emulator.operands.set_offset_u32(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset)
| emulator
.operands
.get_offset_u32(operand_indices::INPUT2, offset),
),
OperandType::I64 | OperandType::F64 => emulator.operands.set_offset_u64(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset)
| emulator
.operands
.get_offset_u64(operand_indices::INPUT2, offset),
),
OperandType::I128 | OperandType::F128 => {
quad_word_not_supported(instruction);
}
}
}
pub fn xor(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
match instruction.operand_type {
OperandType::I8 => emulator.operands.set_offset_u8(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset)
^ emulator
.operands
.get_offset_u8(operand_indices::INPUT2, offset),
),
OperandType::I16 | OperandType::F16 => emulator.operands.set_offset_u16(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset)
^ emulator
.operands
.get_offset_u16(operand_indices::INPUT2, offset),
),
OperandType::I32 | OperandType::F32 => emulator.operands.set_offset_u32(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset)
^ emulator
.operands
.get_offset_u32(operand_indices::INPUT2, offset),
),
OperandType::I64 | OperandType::F64 => emulator.operands.set_offset_u64(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset)
^ emulator
.operands
.get_offset_u64(operand_indices::INPUT2, offset),
),
OperandType::I128 | OperandType::F128 => {
quad_word_not_supported(instruction);
}
}
}
pub fn bit_scan(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
let option_bits = emulator.operands.get_u8(operand_indices::INPUT2);
let highest_bit = (option_bits & 0x1) != 0;
let negative_return = (option_bits & 0x8) != 0;
match instruction.operand_type {
OperandType::I8 => {
let operand = emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset);
if highest_bit {
let index = (u8::BITS - operand.leading_zeros()) as i8 - 1;
emulator.operands.set_offset_i8(
operand_indices::OUTPUT,
offset,
if index < 0 {
0 - negative_return as i8
} else {
index
},
);
} else {
let index = operand.trailing_zeros() + 1;
emulator.operands.set_offset_i8(
operand_indices::OUTPUT,
offset,
if index > u8::BITS {
0 - negative_return as i8
} else {
index as i8
},
);
}
}
OperandType::I16 => {
let operand = emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset);
if highest_bit {
let index = (u16::BITS - operand.leading_zeros()) as i16 - 1;
emulator.operands.set_offset_i16(
operand_indices::OUTPUT,
offset,
if index < 0 {
0 - negative_return as i16
} else {
index
},
);
} else {
let index = operand.trailing_zeros() + 1;
emulator.operands.set_offset_i16(
operand_indices::OUTPUT,
offset,
if index > u16::BITS {
0 - negative_return as i16
} else {
index as i16
},
);
}
}
OperandType::I32 => {
let operand = emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset);
if highest_bit {
let index = (u32::BITS - operand.leading_zeros()) as i32 - 1;
emulator.operands.set_offset_i32(
operand_indices::OUTPUT,
offset,
if index < 0 {
0 - negative_return as i32
} else {
index
},
);
} else {
let index = operand.trailing_zeros() + 1;
emulator.operands.set_offset_i32(
operand_indices::OUTPUT,
offset,
if index > u32::BITS {
0 - negative_return as i32
} else {
index as i32
},
);
}
}
OperandType::I64 => {
let operand = emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset);
if highest_bit {
let index = (u64::BITS - operand.leading_zeros()) as i64 - 1;
emulator.operands.set_offset_i64(
operand_indices::OUTPUT,
offset,
if index < 0 {
0 - negative_return as i64
} else {
index
},
);
} else {
let index = operand.trailing_zeros() + 1;
emulator.operands.set_offset_i64(
operand_indices::OUTPUT,
offset,
if index > u64::BITS {
0 - negative_return as i64
} else {
index as i64
},
);
}
}
OperandType::I128 => {
quad_word_not_supported(instruction);
}
OperandType::F16 | OperandType::F32 | OperandType::F64 | OperandType::F128 => {
no_floating_point(instruction);
}
}
}
pub fn compare(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
if (instruction.option_bits & 0x30) != 0 {
log::debug!("Error: Mask option bits in compare instruction not yet implemented");
instruction.valid = false;
return;
}
let mode = instruction.option_bits & 0x6;
let unsigned = (instruction.option_bits & 0x8) != 0;
let inverted = (instruction.option_bits & 0x1) != 0;
match instruction.operand_type {
OperandType::I8 => {
if unsigned {
let first = emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_u8(operand_indices::INPUT2, offset);
let result = match mode >> 1 {
0 => first == second,
1 => first < second,
2 => first > second,
3 => {
log::debug!("Error: Executed compare with invalid option bits");
instruction.valid = false;
false
}
_ => unreachable!(),
} ^ inverted;
emulator
.operands
.set_offset_u8(operand_indices::OUTPUT, offset, result as u8);
} else {
let first = emulator
.operands
.get_offset_i8(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_i8(operand_indices::INPUT2, offset);
let result = match mode >> 1 {
0 => first == second,
1 => first < second,
2 => first > second,
3 => {
log::debug!("Error: Executed compare with invalid option bits");
instruction.valid = false;
false
}
_ => unreachable!(),
} ^ inverted;
emulator
.operands
.set_offset_u8(operand_indices::OUTPUT, offset, result as u8);
}
}
OperandType::I16 => {
if unsigned {
let first = emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_u16(operand_indices::INPUT2, offset);
let result = match mode >> 1 {
0 => first == second,
1 => first < second,
2 => first > second,
3 => {
log::debug!("Error: Executed compare with invalid option bits");
instruction.valid = false;
false
}
_ => unreachable!(),
} ^ inverted;
emulator
.operands
.set_offset_u16(operand_indices::OUTPUT, offset, result as u16);
} else {
let first = emulator
.operands
.get_offset_i16(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_i16(operand_indices::INPUT2, offset);
let result = match mode >> 1 {
0 => first == second,
1 => first < second,
2 => first > second,
3 => {
log::debug!("Error: Executed compare with invalid option bits");
instruction.valid = false;
false
}
_ => unreachable!(),
} ^ inverted;
emulator
.operands
.set_offset_u16(operand_indices::OUTPUT, offset, result as u16);
}
}
OperandType::I32 => {
if unsigned {
let first = emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_u32(operand_indices::INPUT2, offset);
let result = match mode >> 1 {
0 => first == second,
1 => first < second,
2 => first > second,
3 => {
log::debug!("Error: Executed compare with invalid option bits");
instruction.valid = false;
false
}
_ => unreachable!(),
} ^ inverted;
emulator
.operands
.set_offset_u32(operand_indices::OUTPUT, offset, result as u32);
} else {
let first = emulator
.operands
.get_offset_i32(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_i32(operand_indices::INPUT2, offset);
let result = match mode >> 1 {
0 => first == second,
1 => first < second,
2 => first > second,
3 => {
log::debug!("Error: Executed compare with invalid option bits");
instruction.valid = false;
false
}
_ => unreachable!(),
} ^ inverted;
emulator
.operands
.set_offset_u32(operand_indices::OUTPUT, offset, result as u32);
}
}
OperandType::I64 => {
if unsigned {
let first = emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_u64(operand_indices::INPUT2, offset);
let result = match mode >> 1 {
0 => first == second,
1 => first < second,
2 => first > second,
3 => {
log::debug!("Error: Executed compare with invalid option bits");
instruction.valid = false;
false
}
_ => unreachable!(),
} ^ inverted;
emulator
.operands
.set_offset_u64(operand_indices::OUTPUT, offset, result as u64);
} else {
let first = emulator
.operands
.get_offset_i64(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_i64(operand_indices::INPUT2, offset);
let result = match mode >> 1 {
0 => first == second,
1 => first < second,
2 => first > second,
3 => {
log::debug!("Error: Executed compare with invalid option bits");
instruction.valid = false;
false
}
_ => unreachable!(),
} ^ inverted;
emulator
.operands
.set_offset_u64(operand_indices::OUTPUT, offset, result as u64);
}
}
OperandType::F16 => {
let abs_mask = !(0x8000 * (mode >= 6) as u16);
let first = half::f16::from_bits(
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset)
& abs_mask,
);
let second = half::f16::from_bits(
emulator
.operands
.get_offset_u16(operand_indices::INPUT2, offset)
& abs_mask,
);
let result = first
.partial_cmp(&second)
.map_or(unsigned, |order| match mode >> 1 {
0 => order.is_eq() ^ inverted,
1 | 3 => order.is_lt() ^ inverted,
2 => order.is_gt() ^ inverted,
_ => unreachable!(),
});
emulator
.operands
.set_offset_u16(operand_indices::OUTPUT, offset, result as u16);
}
OperandType::F32 => {
let first = emulator
.operands
.get_offset_f32(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_f32(operand_indices::INPUT2, offset);
let (first, second) = if mode >= 6 {
(first.abs(), second.abs())
} else {
(first, second)
};
let result = first
.partial_cmp(&second)
.map_or(unsigned, |order| match mode >> 1 {
0 => order.is_eq() ^ inverted,
1 | 3 => order.is_lt() ^ inverted,
2 => order.is_gt() ^ inverted,
_ => unreachable!(),
});
emulator
.operands
.set_offset_u32(operand_indices::OUTPUT, offset, result as u32);
}
OperandType::F64 => {
let first = emulator
.operands
.get_offset_f64(operand_indices::INPUT1, offset);
let second = emulator
.operands
.get_offset_f64(operand_indices::INPUT2, offset);
let (first, second) = if mode >= 6 {
(first.abs(), second.abs())
} else {
(first, second)
};
let result = first
.partial_cmp(&second)
.map_or(unsigned, |order| match mode >> 1 {
0 => order.is_eq() ^ inverted,
1 | 3 => order.is_lt() ^ inverted,
2 => order.is_gt() ^ inverted,
_ => unreachable!(),
});
emulator
.operands
.set_offset_u64(operand_indices::OUTPUT, offset, result as u64);
}
OperandType::I128 | OperandType::F128 => {
quad_word_not_supported(instruction);
}
}
}
pub fn pop_count(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
match instruction.operand_type {
OperandType::I8 => emulator.operands.set_offset_u8(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset)
.count_ones() as u8,
),
OperandType::I16 => emulator.operands.set_offset_u16(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset)
.count_ones() as u16,
),
OperandType::I32 => emulator.operands.set_offset_u32(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset)
.count_ones(),
),
OperandType::I64 => emulator.operands.set_offset_u64(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset)
.count_ones() as u64,
),
OperandType::I128 => {
quad_word_not_supported(instruction);
}
OperandType::F16 | OperandType::F32 | OperandType::F64 | OperandType::F128 => {
no_floating_point(instruction);
}
}
}
pub fn shift_left(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
match instruction.operand_type {
OperandType::I8 => emulator.operands.set_offset_u8(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset)
.wrapping_shl(
emulator
.operands
.get_offset_u8(operand_indices::INPUT2, offset) as u32,
),
),
OperandType::I16 => emulator.operands.set_offset_u16(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset)
.wrapping_shl(
emulator
.operands
.get_offset_u16(operand_indices::INPUT2, offset) as u32,
),
),
OperandType::I32 => emulator.operands.set_offset_u32(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset)
.wrapping_shl(
emulator
.operands
.get_offset_u32(operand_indices::INPUT2, offset),
),
),
OperandType::I64 => emulator.operands.set_offset_u64(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset)
.wrapping_shl(
emulator
.operands
.get_offset_u32(operand_indices::INPUT2, offset),
),
),
OperandType::I128 => {
quad_word_not_supported(instruction);
}
OperandType::F16 | OperandType::F32 | OperandType::F64 | OperandType::F128 => {
no_floating_point(instruction);
}
}
}
pub fn shift_right_u(
emulator: &mut ForwardComEmulator,
instruction: &mut EmulatorInstruction,
offset: usize,
) {
match instruction.operand_type {
OperandType::I8 => emulator.operands.set_offset_u8(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u8(operand_indices::INPUT1, offset)
.wrapping_shr(
emulator
.operands
.get_offset_u8(operand_indices::INPUT2, offset) as u32,
),
),
OperandType::I16 => emulator.operands.set_offset_u16(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u16(operand_indices::INPUT1, offset)
.wrapping_shr(
emulator
.operands
.get_offset_u16(operand_indices::INPUT2, offset) as u32,
),
),
OperandType::I32 => emulator.operands.set_offset_u32(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u32(operand_indices::INPUT1, offset)
.wrapping_shr(
emulator
.operands
.get_offset_u32(operand_indices::INPUT2, offset),
),
),
OperandType::I64 => emulator.operands.set_offset_u64(
operand_indices::OUTPUT,
offset,
emulator
.operands
.get_offset_u64(operand_indices::INPUT1, offset)
.wrapping_shr(
emulator
.operands
.get_offset_u32(operand_indices::INPUT2, offset),
),
),
OperandType::I128 => {
quad_word_not_supported(instruction);
}
OperandType::F16 | OperandType::F32 | OperandType::F64 | OperandType::F128 => {
no_floating_point(instruction);
}
}
}