use super::*;
use crate::emulator::core::{
DecodedComplexInstruction, DecodedInstruction, MemoryOperand, Operand, OperandExtension,
OperandType,
};
use crate::emulator::RegisterFile;
use crate::emulator::instructions::SimpleIsaInstruction;
use gpcas_base::instruction_type;
use gpcas_isa::instruction_flags;
use std::collections::VecDeque;
pub fn push(
instruction: DecodedComplexInstruction,
register_file: &RegisterFile,
_memory: &[u8],
output: &mut VecDeque<DecodedInstruction>,
) {
let last_register = instruction.option_bits & 0x1F;
let forwards = (instruction.option_bits & 0x80) != 0;
let mut push_address =
register_file.general_purpose[instruction.stack_pointer as usize] as usize;
if instruction.vector_instruction {
if forwards {
panic!("Tried to push a vector on a forward-facing stack");
} else {
for register in instruction.data_register..=last_register as u32 {
let vector_length = register_file.vector_lengths[register as usize];
push_address -= vector_length + 8;
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: &SimpleIsaInstruction {
function: sub,
inputs: 3,
instr_type: instruction_type::INT_ADD,
..SimpleIsaInstruction::DEFAULT
},
operands: [
Operand::GeneralPurposeRegister(instruction.stack_pointer),
Operand::Immediate(vector_length as u64 + 8),
Operand::VectorRegister(register),
Operand::None,
],
operand_type: OperandType::I64,
operand_count: 3,
..Default::default()
});
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: &SimpleIsaInstruction {
function: move_instruction,
inputs: 1,
instr_type: instruction_type::MOVE,
instruction_flags: instruction_flags::MEM_IN | instruction_flags::MEM_OUT,
..SimpleIsaInstruction::DEFAULT
},
operands: [
Operand::Immediate(vector_length as u64),
Operand::Memory(MemoryOperand {
register_count: 1,
registers: [instruction.stack_pointer, 0],
}),
Operand::None,
Operand::None,
],
operand_type: OperandType::I64,
operand_count: 2,
memory_address: push_address,
..Default::default()
});
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: &SimpleIsaInstruction {
function: move_instruction,
inputs: 1,
instr_type: instruction_type::MOVE,
instruction_flags: instruction_flags::MEM_IN | instruction_flags::MEM_OUT,
..SimpleIsaInstruction::DEFAULT
},
operands: [
Operand::VectorRegister(register),
Operand::Memory(MemoryOperand {
register_count: 1,
registers: [instruction.stack_pointer, 0],
}),
Operand::None,
Operand::None,
],
operand_type: OperandType::I64,
operand_count: 2,
memory_address: push_address + 8,
operand_extension: OperandExtension::Default(vector_length as u16),
..Default::default()
});
}
}
} else {
let pointer_update_instruction: &SimpleIsaInstruction;
let address_offset;
let address_factor;
if forwards {
pointer_update_instruction = &POINTER_ADD;
address_offset = 0;
address_factor = 1;
} else {
pointer_update_instruction = &POINTER_SUB;
address_offset = 1;
address_factor = -1;
}
for register in instruction.data_register..=last_register as u32 {
let i = (register - instruction.data_register) as isize;
let memory_address = (push_address as isize
+ (((i + address_offset) * address_factor) << instruction.operand_type.log_size()))
as usize;
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: &SimpleIsaInstruction {
function: move_instruction,
inputs: 1,
instr_type: instruction_type::MOVE,
instruction_flags: instruction_flags::MEM_IN | instruction_flags::MEM_OUT,
..SimpleIsaInstruction::DEFAULT
},
operands: [
Operand::GeneralPurposeRegister(register),
Operand::Memory(MemoryOperand {
register_count: 1,
registers: [instruction.stack_pointer, 0],
}),
Operand::None,
Operand::None,
],
operand_type: instruction.operand_type,
operand_count: 2,
memory_address,
..Default::default()
});
}
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: pointer_update_instruction,
operands: [
Operand::GeneralPurposeRegister(instruction.stack_pointer),
Operand::Immediate(
(last_register as u64 - instruction.data_register as u64 + 1)
<< instruction.operand_type.log_size(),
),
Operand::None,
Operand::None,
],
operand_type: instruction.operand_type,
operand_count: 2,
..Default::default()
})
}
}
pub fn pop(
instruction: DecodedComplexInstruction,
register_file: &RegisterFile,
memory: &[u8],
output: &mut VecDeque<DecodedInstruction>,
) {
let last_register = instruction.option_bits & 0x1F;
let forwards = (instruction.option_bits & 0x40) != 0;
let mut pop_address =
register_file.general_purpose[instruction.stack_pointer as usize] as usize;
if instruction.vector_instruction {
if forwards {
panic!("Tried to push a vector on a forward-facing stack");
} else {
for register in (instruction.data_register..=last_register as u32).rev() {
let vector_length =
u64::from_le_bytes(memory[pop_address..pop_address + 8].try_into().unwrap())
as usize;
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: &SimpleIsaInstruction {
function: move_instruction,
inputs: 1,
instr_type: instruction_type::MOVE,
instruction_flags: 0,
..SimpleIsaInstruction::DEFAULT
},
operands: [
Operand::Memory(MemoryOperand {
register_count: 1,
registers: [instruction.stack_pointer, 0],
}),
Operand::None,
Operand::None,
Operand::None,
],
operand_type: OperandType::I64,
operand_count: 1,
memory_address: pop_address,
..Default::default()
});
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: &SimpleIsaInstruction {
function: move_instruction,
inputs: 1,
instr_type: instruction_type::MOVE,
..SimpleIsaInstruction::DEFAULT
},
operands: [
Operand::VectorRegister(register),
Operand::Memory(MemoryOperand {
register_count: 1,
registers: [instruction.stack_pointer, 0],
}),
Operand::None,
Operand::None,
],
operand_type: OperandType::I64,
operand_count: 2,
memory_address: pop_address + 8,
operand_extension: OperandExtension::Default(vector_length as u16),
..Default::default()
});
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: &SimpleIsaInstruction {
function: add,
inputs: 2,
instr_type: instruction_type::INT_ADD,
..SimpleIsaInstruction::DEFAULT
},
operands: [
Operand::GeneralPurposeRegister(instruction.stack_pointer),
Operand::Immediate(vector_length as u64 + 8),
Operand::None,
Operand::None,
],
operand_type: OperandType::I64,
operand_count: 3,
..Default::default()
});
pop_address += vector_length + 8;
}
}
} else {
let pointer_update_instruction: &SimpleIsaInstruction;
let address_offset;
let address_factor;
if forwards {
pointer_update_instruction = &POINTER_SUB;
address_offset = 1;
address_factor = -1;
} else {
pointer_update_instruction = &POINTER_ADD;
address_offset = 0;
address_factor = 1;
}
for register in (instruction.data_register..=last_register as u32).rev() {
let i = (last_register as u32 - register) as isize;
let memory_address = (pop_address as isize
+ (((i + address_offset) * address_factor) << instruction.operand_type.log_size()))
as usize;
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: &SimpleIsaInstruction {
function: move_instruction,
inputs: 1,
instr_type: instruction_type::MOVE,
..SimpleIsaInstruction::DEFAULT
},
operands: [
Operand::GeneralPurposeRegister(register),
Operand::Memory(MemoryOperand {
register_count: 1,
registers: [instruction.stack_pointer, 0],
}),
Operand::None,
Operand::None,
],
operand_type: instruction.operand_type,
operand_count: 2,
memory_address,
..Default::default()
});
}
output.push_back(DecodedInstruction {
address: instruction.address,
size: 4,
instruction: pointer_update_instruction,
operands: [
Operand::GeneralPurposeRegister(instruction.stack_pointer),
Operand::Immediate(
(last_register as u64 - instruction.data_register as u64 + 1)
<< instruction.operand_type.log_size(),
),
Operand::None,
Operand::None,
],
operand_type: instruction.operand_type,
operand_count: 2,
..Default::default()
})
}
}
const POINTER_ADD: SimpleIsaInstruction = SimpleIsaInstruction {
function: add,
inputs: 2,
instr_type: instruction_type::INT_ADD,
..SimpleIsaInstruction::DEFAULT
};
const POINTER_SUB: SimpleIsaInstruction = SimpleIsaInstruction {
function: sub,
inputs: 2,
instr_type: instruction_type::INT_ADD,
..SimpleIsaInstruction::DEFAULT
};