/**
 * 
 */
package dspl.backend.x86;

import dspl.SymbolTable;
import dspl.backend.AssemblyfileGenerator;
import dspl.backend.CCallInstructionProcessor;
import dspl.backend.InstructionProcessor;
import dspl.backend.x86.operands.X86Operand;
import dspl.backend.x86.operands.X86Register;
import dspl.intermediate.instructions.CCallInstruction;
import dspl.intermediate.variable.Variable;

/**
 * CCallInstructionProcess for X86 ISA. Responsible for converting
 * CCall instructions into the appriopriate series of instructions
 * pushing the arguements onto the stack, calling the function,
 * and lastly adjusting the stack (popping elements)
 * @author David
 */
public class X86CCallInstructionProcessor extends CCallInstructionProcessor {

	/* (non-Javadoc)
	 * @see dspl.backend.CCallInstructionProcessor#processCCallInstruction(dspl.intermediate.instructions.CCallInstruction, dspl.backend.InstructionProcessor, dspl.SymbolTable, dspl.backend.AssemblyfileGenerator)
	 */
	public void processCCallInstruction(CCallInstruction inst,
		InstructionProcessor ip, SymbolTable st, AssemblyfileGenerator afg) {
		Variable[] parameters = inst.getParameters(); 
		int operandsSize = 0; 
		X86Operand op; 
		
		//push the arguements onto the stack, backwards, since the C-function called will pop them off
		for (int i=parameters.length-1; i>=0; i--) { 
			Variable curVar = parameters[i];
			//TODO (Someone): Note: no passing of arrays or complex types in ccall
			if (curVar.isArray()) {
				throw new Error("no passing arrays");
			}
			if (curVar.getDataType()==Variable.DATA_TYPE_COMPLEX) { 
				throw new Error("no passing complex types");
			}
			
			//push only works with 16bit or greater values, so if byte, then move into eax first
			if (curVar.getDataType()==Variable.DATA_TYPE_BYTE || curVar.getDataType()==Variable.DATA_TYPE_UBYTE) {
				op = X86VariableManager.prepareArg1Variable(curVar, X86Operand.OPERAND_TYPE_INDEXED_MEMORY | X86Operand.OPERAND_TYPE_MEMORY | X86Operand.OPERAND_TYPE_IMMEDIATE, afg); //loads it as a non-reg
				X86Operand eaxReg = X86VariableManager.getEax(); 
				//((X86Register)eaxReg).setRegisterContentsSize(1); 
				if (curVar.getDataType()==Variable.DATA_TYPE_UBYTE) {
					afg.writeAsmInstr("movzbl", op.toString(), eaxReg.toString()); //movsbl op, eaxReg
				}
				else if (curVar.getDataType()==Variable.DATA_TYPE_BYTE) {
					afg.writeAsmInstr("movsbl", op.toString(), eaxReg.toString()); //movsbl op, eaxReg
				}
				afg.writeAsmInstr("pushl", eaxReg.toString()); //always push with 32 bit operand, probably an ABI 4byte stack alignment issue as well! (can also do 16, but we don't have 16 bit types) :)
				operandsSize += 4; 
			}
			else if (curVar.getDataType()==Variable.DATA_TYPE_FLOAT) {
				//nice explinatin of stack @ http://www.milw0rm.org/papers/50
				X86VariableManager.pushFloatingPointVariable(curVar, afg);  //push the FP onto the FPU stack
				afg.writeAsmInstr("subl", "$8", "%esp"); //decrement the regular stack by 8
				afg.writeAsmInstr("fstpl", "(%esp)"); //write top of stack (and pop) to float
				operandsSize += 4; 
			}
			else {
				op = X86VariableManager.prepareArg1Variable(curVar, X86Operand.OPERAND_CLASS_ANY, afg); //loads it
				afg.writeAsmInstr(X86AsmFormatter.getInstr("push", curVar), op.toString());
				operandsSize += 4; //everything else should be four too
			}
		}
		
		//TODO: (Dave) need to potentially store registers which might be modified by calling another function which are currently in use
		afg.writeAsmInstr("call",("_" + inst.funcName));
		
		//clean up stack after having pushed arguements onto them
		afg.writeAsmInstr("addl", ("$" + operandsSize), "%esp"); 
	}

}
