use super::decode::DecodedInstruction;
use crate::emulator::constants::{default_values, operand_indices, register_indices};
use crate::emulator::core::decode::{Operand, OperandExtension};
use crate::emulator::EmulatorInstruction;
use crate::ForwardComEmulator;
use gpcas_base::instruction_type;
use gpcas_isa::{instruction_flags, Instruction, IS_VECTOR_REGISTER};
pub fn prepare(
emulator: &mut ForwardComEmulator,
instruction: DecodedInstruction,
has_follow_up: bool,
commit: bool,
) -> EmulatorInstruction {
let DecodedInstruction {
address,
size,
instruction,
mut operands,
operand_type,
operand_count,
memory_address,
branch_address,
mask_index,
option_bits,
operand_extension,
} = instruction;
let operand_type = if let Some(new_type) = instruction.operand_type_override {
new_type
} else {
operand_type
};
if let Some((index, operand_kind)) = &instruction.register_type_override {
operands[*index as usize] = operands[*index as usize]
.clone()
.override_type(operand_kind);
}
let instr_type = if operand_type.is_float() {
match instruction.instr_type {
instruction_type::INT_ADD => instruction_type::FLOAT_ADD,
instruction_type::INT_MUL => instruction_type::FLOAT_MUL,
instruction_type::INT_DIV => instruction_type::FLOAT_DIV,
instruction_type::INT_MUL_ADD => instruction_type::FLOAT_MUL_ADD,
other => other,
}
} else {
instruction.instr_type
};
let special_output;
let output;
let element_size = 1 << operand_type.log_size();
let mut vector_size = None;
let mut flags = instruction.instruction_flags;
let mut inputs = [0; 4];
let mut input_register_count = 0;
let mut memory_register_count = 0;
let mut memory_operand_size = 0;
if (operand_count > 0) & (instruction.inputs <= operand_count) {
emulator.operands.clear();
let mut operand_index = operand_count as isize - 1;
let mut input_index = instruction.inputs as i8 - 1;
while (operand_index >= 0) & (input_index >= 0) {
match &operands[operand_index as usize] {
Operand::None => panic!("Tried to read non-existent format operand"),
Operand::GeneralPurposeRegister(index) => {
inputs[input_register_count as usize] = *index as u8;
input_register_count += 1;
emulator.operands.set_u64(
operand_indices::INPUT1 + input_index as usize,
emulator.registers.general_purpose[*index as usize],
);
input_index -= 1;
}
Operand::SpecialRegister(index) => {
inputs[input_register_count as usize] = *index as u8;
input_register_count += 1;
emulator.operands.set_u64(
operand_indices::INPUT1 + input_index as usize,
emulator.registers.special[*index as usize],
);
input_index -= 1;
}
Operand::VectorRegister(index) => {
if (flags & instruction_flags::MEM_OUT) == 0 {
vector_size = Some(emulator.registers.vector_lengths[*index as usize]);
}
inputs[input_register_count as usize] = IS_VECTOR_REGISTER | *index as u8;
input_register_count += 1;
emulator.operands.set(
operand_indices::INPUT1 + input_index as usize,
emulator.registers.get_vector(*index as usize),
);
input_index -= 1;
}
Operand::Immediate(value) => match operand_extension {
OperandExtension::Broadcast(length) => {
let length =
std::cmp::min(length as usize, emulator.operands.operand_size());
for i in 0..length >> operand_type.log_size() {
emulator.operands.set_offset(
operand_indices::INPUT1 + input_index as usize,
i << operand_type.log_size(),
&value.to_le_bytes()[0..element_size],
);
}
input_index -= 1;
}
_ => {
emulator
.operands
.set_u64(operand_indices::INPUT1 + input_index as usize, *value);
input_index -= 1;
}
},
Operand::Memory(operand) => {
flags ^= instruction_flags::MEM_IN;
inputs[input_register_count as usize] = operand.registers[0] as u8;
inputs[input_register_count as usize + 1] = operand.registers[1] as u8;
input_register_count = operand.register_count;
memory_register_count = operand.register_count;
match operand_extension {
OperandExtension::None | OperandExtension::Scalar => {
memory_operand_size = element_size as u16;
if commit {
emulator.operands.set(
operand_indices::INPUT1 + input_index as usize,
&emulator.memory[memory_address..memory_address + element_size],
);
}
}
OperandExtension::Default(length) => {
let length =
std::cmp::min(length as usize, emulator.operands.operand_size());
vector_size = Some(length);
flags ^= instruction_flags::MEM_VECTOR;
memory_operand_size = length as u16;
if commit {
emulator.operands.set(
operand_indices::INPUT1 + input_index as usize,
&emulator.memory[memory_address..memory_address + length],
);
}
}
OperandExtension::Broadcast(length) => {
let length =
std::cmp::min(length as usize, emulator.operands.operand_size());
vector_size = Some(length);
memory_operand_size = element_size as u16;
if commit {
for i in 0..length >> operand_type.log_size() {
emulator.operands.set_offset(
operand_indices::INPUT1 + input_index as usize,
i << operand_type.log_size(),
&emulator.memory
[memory_address..memory_address + element_size],
);
}
}
}
}
if (flags & instruction_flags::MEM_OUT) == 0 {
input_index -= 1;
}
}
}
operand_index -= 1;
}
if (flags & instruction_flags::REG_OUT) != 0 {
match operands[0] {
Operand::GeneralPurposeRegister(register_index) => {
special_output = false;
output = register_index as u8;
}
Operand::SpecialRegister(register_index) => {
special_output = true;
output = register_index as u8;
}
Operand::VectorRegister(register_index) => {
special_output = false;
output = IS_VECTOR_REGISTER | register_index as u8;
}
_ => panic!("Format with invalid output operand type"),
}
} else {
special_output = false;
output = 0;
}
} else {
special_output = false;
output = 0;
}
let operand_size = vector_size.unwrap_or(element_size);
flags |= instruction_flags::HAS_FOLLOW_UP * has_follow_up as u16;
let mut valid = true;
if mask_index != default_values::MASK {
if (output & IS_VECTOR_REGISTER) != 0 {
inputs[input_register_count as usize] = IS_VECTOR_REGISTER | mask_index;
input_register_count += 1;
emulator.operands.set(
operand_indices::MASK,
emulator.registers.get_vector(mask_index as usize),
);
} else {
inputs[input_register_count as usize] = mask_index;
input_register_count += 1;
emulator.operands.set_u64(
operand_indices::MASK,
emulator.registers.general_purpose[mask_index as usize],
);
}
let output_offset = ((flags & instruction_flags::REG_OUT) != 0) as u8;
let fallback_index = if instruction.inputs >= 3 {
operand_count as usize - std::cmp::min(instruction.inputs, operand_count) as usize
} else if operand_count > instruction.inputs + output_offset + 1 {
(operand_count - instruction.inputs) as usize - 1
} else if operand_count > instruction.inputs + output_offset {
output_offset as usize
} else {
(operand_count - instruction.inputs) as usize - 1
};
if fallback_index != default_values::FALLBACK_REGISTER {
match operands[fallback_index] {
Operand::GeneralPurposeRegister(register_index) => {
inputs[input_register_count as usize] = fallback_index as u8;
input_register_count += 1;
emulator.operands.set_u64(
operand_indices::FALLBACK,
emulator.registers.general_purpose[register_index as usize],
);
}
Operand::VectorRegister(register_index) => {
inputs[input_register_count as usize] =
IS_VECTOR_REGISTER | fallback_index as u8;
input_register_count += 1;
emulator.operands.set(
operand_indices::FALLBACK,
emulator.registers.get_vector(register_index as usize),
);
}
_ => {
log::debug!("Error: Tried to read an invalid fallback type");
valid = false;
}
}
}
} else {
emulator.operands.set_broadcast(
operand_indices::MASK,
&emulator.registers.special[register_indices::NUMCONTR].to_le_bytes(),
);
}
EmulatorInstruction {
core: Instruction {
address,
memory_address,
branch_address,
instr_type,
memory_operand_size,
flags,
inputs,
input_register_count,
memory_register_count,
output,
size,
},
function: instruction.function,
vector_mode: instruction.vector_mode,
operand_type,
operand_size,
option_bits,
special_output,
valid,
}
}