//Emitting code ;)
#pragma warning(disable:4127)
#pragma warning(disable:4244)
#pragma warning(disable:4245)

#include "../types.h"
#include "x86_emitter.h"
bool IsS8(u32 value)
{
	if (((value&0xFFFFFF80)==0xFFFFFF80) ||
		(value&0xFFFFFF80)==0  )
		return true;
	else
		return false;
}
#include "x86_op_encoder.h"
#include "x86_matcher.h"
//x86_Label 
/*
//x86_ptr/x86_ptr_imm
x86_ptr x86_ptr::create(void* ptr)
{
	x86_ptr rv={ptr};
	return rv;
}*/
x86_ptr x86_ptr::create(size_t ptr)
{
#pragma warning(disable:4312)
	x86_ptr rv(0);
	rv.ptr_int=ptr;
	return rv;
#pragma warning(default:4312) 
}
/*
x86_ptr_imm x86_ptr_imm::create(void* ptr)
{
	x86_ptr_imm rv={ptr};
	return rv;
}*/
x86_ptr_imm x86_ptr_imm::create(size_t ptr)
{
#pragma warning(disable:4312) 
	x86_ptr_imm rv(0);
	rv.ptr_int=ptr;
	return rv;
#pragma warning(default:4312) 
}
//x86_block
//init things

#ifdef X86_OP_NAMES
const char Names[op_count][64] =
{
	#include "generated_class_names_string.h"
};
#endif


void* x86_Label::GetPtr()
{
	return owner->x86_buff + this->target_opcode;
}

const char* DissasmClass(x86_opcode_class opcode)
{
	#ifdef X86_OP_NAMES
	return Names[opcode];
	#else
	return "No opcode name info included";
	#endif
}
void x86_block::Init(dyna_reallocFP* ral,dyna_finalizeFP* alf)
{
	ralloc=ral;
	allocfin=alf;
	x86_buff=0;
	x86_indx=0;
	x86_size=0;
	do_realloc=true;
}
#define patches (*(vector<code_patch>*) _patches)
#define labels (*(vector<x86_Label*>*) _labels)

//Generates code.if user_data is non zero , user_data_size bytes are allocated after the executable code
//and user_data is set to the first byte of em.Allways 16 byte aligned
void* x86_block::Generate()
{
	if (do_realloc)
	{
		u8* final_buffer=0;

		final_buffer=(u8*)allocfin(x86_buff,x86_size,x86_indx);

		if (final_buffer==0)
			return 0;

		x86_buff=final_buffer;
	}
	ApplyPatches(x86_buff);

	return &x86_buff[0];
}

struct x86_block_externs_i : x86_block_externs
{
	struct extern_entry { u8* dst;u32 offs:28;u32 size:4; };
	vector<extern_entry> externs;

	void Apply(u8* base)
	{
		for (u32 i=0;i<externs.size();i++)
		{
			u8* dest=(u8*)externs[i].dst;

			u8* code_offset=base+externs[i].offs;
			u8* diff_offset=code_offset+externs[i].size;

			u32 diff=(u32)(dest-diff_offset);

         switch (externs[i].size)
         {
            case 1:
               verify(IsS8(diff));
               *code_offset=(u8)diff;
               break;
            case 2:
               *(u16*)code_offset=(u16)diff;
               break;
            case 4:
               *(u32*)code_offset=(u32)diff;
               break;
         }
		}
	}
	bool Modify(u32 offs,u8* dst)
	{
		for (u32 i=0;i<externs.size();i++)
		{
			if (externs[i].offs==offs)
			{
				externs[i].dst=dst;
				return true;
			}
		}
		return false;
	}
	void Add(u8* dst,u32 offset,u32 size)
	{
		extern_entry vta={dst,offset,size};
		externs.push_back(vta);
	}
	void Free()
	{
		delete this;
	}
};

x86_block_externs::~x86_block_externs() {}
void x86_block_externs::Apply(void* lolwhut) { ((x86_block_externs_i*)this)->Apply((u8*)lolwhut); }
bool x86_block_externs::Modify(u32 offs,u8* dst) { return ((x86_block_externs_i*)this)->Modify(offs,dst); }
void x86_block_externs::Free() { ((x86_block_externs_i*)this)->Free(); }

x86_block_externs* x86_block::GetExterns()
{
	x86_block_externs_i* rv=new x86_block_externs_i();
	u8* base=x86_buff;
	for (u32 i=0;i<patches.size();i++)
	{
		u8* dest=(u8*)patches[i].dest;

		if (patches[i].type&16)
		{
			if (patches[i].lbl->owner==this)
				dest = base + patches[i].lbl->target_opcode;
			else
				dest = patches[i].lbl->owner->x86_buff + patches[i].lbl->target_opcode;
		}

		u32 start_diff=(u32)(dest-base);
		if (start_diff>=x86_indx)
		{
			rv->Add(dest,patches[i].offset,patches[i].type&0xF);
		}
	}

	return rv;
}

#if 0
#include "windows.h"
/*void x86_block::CopyTo(void* to)
{
	memcpy(to,x86_buff,x86_indx);
	free(x86_buff);
	x86_buff=(u8*)to;

	ApplyPatches(x86_buff);
}
*/

#endif

//wut ?
void x86_block::ApplyPatches(u8* base)
{
	for (u32 i=0;i<patches.size();i++)
	{
		u8* dest=(u8*)patches[i].dest;

		u8* code_offset=base+patches[i].offset;
		u8* diff_offset=code_offset+(patches[i].type&0xF);

		if (patches[i].type&16)
		{
			if (patches[i].lbl->owner==this)
				dest = base + patches[i].lbl->target_opcode;
			else
				dest = patches[i].lbl->owner->x86_buff + patches[i].lbl->target_opcode;

		}

		u32 diff=(u32)(dest-diff_offset);
      u32 patch_type_size = patches[i].type & 0xF;

      switch (patch_type_size)
      {
         case 1:
            verify(IsS8(diff));
            *code_offset=(u8)diff;
            break;
         case 2:
            *(u16*)code_offset=(u16)diff;
            break;
         case 4:
            *(u32*)code_offset=(u32)diff;
            break;
      }
	}
}
x86_block::x86_block()
{
	_patches=new vector<code_patch>;
	_labels=new vector<x86_Label*>;
	opcode_count=0;
}
x86_block::~x86_block()
{
	//ensure everything is freed :)
	Free();
	delete &patches;
	delete &labels;
}
//Will free any used resources except generated code
void x86_block::Free()
{
	for (u32 i =0;i<labels.size();i++)
		delete labels[i];
	labels.clear();
}
void x86_block::x86_buffer_ensure(u32 size)
{
	if (this->x86_size<(size+x86_indx))
	{
		verify(do_realloc!=false);
		u32 old_size=x86_size;
		x86_size+=128;
		x86_size*=2;
		x86_buff=(u8*)ralloc(x86_buff,old_size,x86_size);
	}
}
void  x86_block::write8(u32 value)
{
	x86_buffer_ensure(15);
	//printf("%02X ",value);
	x86_buff[x86_indx]=value;
	x86_indx+=1;
}
void  x86_block::write16(u32 value)
{
	x86_buffer_ensure(15);
	//printf("%04X ",value);
	*(u16*)&x86_buff[x86_indx]=value;
	x86_indx+=2;
}
void  x86_block::write32(u32 value)
{
	x86_buffer_ensure(15);
	//printf("%08X ",value);
	*(u32*)&x86_buff[x86_indx]=value;
	x86_indx+=4;
}

//Label related code

//NOTE : Label position in mem must not change
void x86_block::CreateLabel(x86_Label* lbl,bool mark,u32 sz)
{
	memset(lbl,0xFFFFFFFF,sizeof(x86_Label));
	lbl->owner=this;
	lbl->marked=false;
	lbl->patch_sz=sz;
	if (mark)
		MarkLabel(lbl);
}
//Allocate a label and create it :).Will be deleted when calling free and/or destructor
x86_Label* x86_block::CreateLabel(bool mark,u32 sz)
{
	x86_Label* lbl = new x86_Label();
	CreateLabel(lbl,mark,sz);
	labels.push_back(lbl);
	return lbl;
}
//Mark a label so that it points to next emitted opcode
void x86_block::MarkLabel(x86_Label* lbl)
{
	verify(lbl->marked==false);
	lbl->marked=true;
	lbl->target_opcode=x86_indx;
	//lbl->target_opcode=(u32)opcodes.size();
}
//opcode Emitters

x86_mrm_t c_mrm(x86_ptr mem)
{
	return x86_mrm(NO_REG,mem);
}
//no param
void x86_block::Emit(x86_opcode_class op)
{
	ME_op_0(op);
}

//1 param
//reg
void x86_block::Emit(x86_opcode_class op,x86_reg reg)
{
	ME_op_1_nrm(op,reg);
}
//smrm
void x86_block::Emit(x86_opcode_class op,x86_ptr mem)
{
	Emit(op,c_mrm(mem));
}
//mrm
void x86_block::Emit(x86_opcode_class op,x86_mrm_t mrm)
{
	ME_op_1_nrm(op,mrm);
}
//imm
void x86_block::Emit(x86_opcode_class op,u32 imm)
{
	ME_op_1_imm(op,imm);
}
//ptr_imm
void x86_block::Emit(x86_opcode_class op,x86_ptr_imm disp)
{
	ME_op_1_nrm(op,disp);
}
//lbl
void x86_block::Emit(x86_opcode_class op,x86_Label* lbl)
{
	
	ME_op_1_nrm(op,lbl);
}

//2 param
//reg,reg, reg1 is written
void x86_block::Emit(x86_opcode_class op,x86_reg reg1,x86_reg reg2)
{
	ME_op_2_nrm(op,reg1,reg2);
}
//reg,smrm, reg is written
void x86_block::Emit(x86_opcode_class op,x86_reg reg,x86_ptr mem)
{
	Emit(op,reg,c_mrm(mem));
}
//reg,mrm, reg is written
void x86_block::Emit(x86_opcode_class op,x86_reg reg,x86_mrm_t mrm)
{
	ME_op_2_nrm(op,reg,mrm);
}
//reg,imm, reg is written
void x86_block::Emit(x86_opcode_class op,x86_reg reg,u32 imm)
{
	ME_op_2_imm(op,reg,imm);
}

//smrm,reg, mem is written
void x86_block::Emit(x86_opcode_class op,x86_ptr mem,x86_reg reg)
{
	Emit(op,c_mrm(mem),reg);
}
//smrm,imm, mem is written
void x86_block::Emit(x86_opcode_class op,x86_ptr mem,u32 imm)
{
	Emit(op,c_mrm(mem),imm);
}

//mrm,reg, mrm is written
void x86_block::Emit(x86_opcode_class op,x86_mrm_t mrm,x86_reg reg)
{
	ME_op_2_nrm(op,mrm,reg);
}
//mrm,imm, mrm is written
void x86_block::Emit(x86_opcode_class op,x86_mrm_t mrm,u32 imm)
{
	ME_op_2_imm(op,mrm,imm);
}

//3 param
//reg,reg,imm, reg1 is written
void x86_block::Emit(x86_opcode_class op,x86_reg reg1,x86_reg reg2,u32 imm)
{
	ME_op_3_imm(op,reg1,reg2,imm);
}

//reg,mrm,imm, reg1 is written
void x86_block::Emit(x86_opcode_class op,x86_reg reg,x86_ptr mem,u32 imm)
{
	//GCC bitches about using this directly. It doesn't complain for the other uses though
	//go figure ....
	x86_mrm_t mrm = c_mrm(mem);
	ME_op_3_imm(op,reg,mrm,imm);
}

//reg,mrm,imm, reg1 is written
void x86_block::Emit(x86_opcode_class op,x86_reg reg,x86_mrm_t mrm,u32 imm)
{
	ME_op_3_imm(op,reg,mrm,imm);
}

#define make_modrm(mod,rm) ( ((mod)<<6) | ((0)<<3) | (rm) )
#define make_sib(scale,index,base) ( ((scale)<<6) | ((index)<<3) | (base) )

u8 EncodeDisp(u32 disp,x86_mrm_t* to,u8 flags)
{
	//[reg+sdisp8] or [reg+sdisp32]
	//sdisp32 support only for now , sdisp8 for later
	if (flags&1)
	{
		if (IsS8(disp))
		{
			to->flags|=2;
			to->disp=disp;
			if (flags&4)
				return 0;
			else
				return make_modrm(1,0);
		}
	}
	if (flags&2)
	{
		to->flags|=4;
		to->disp=disp;
		if (flags&4)
			return 0;
		else
			return make_modrm(2,0);
	}
	verify(false);
	return 0;
}
/*__declspec(dllexport) */x86_mrm_t x86_mrm(x86_reg base)
{
	return x86_mrm(base,NO_REG,sib_scale_1,0);
}
/*__declspec(dllexport) */x86_mrm_t x86_mrm(x86_reg base,x86_ptr disp)
{
	return x86_mrm(base,NO_REG,sib_scale_1,disp);
}
/*__declspec(dllexport) */x86_mrm_t x86_mrm(x86_reg index,x86_sib_scale scale,x86_ptr disp)
{
	return x86_mrm(NO_REG,index,scale,disp);
}
/*__declspec(dllexport) */x86_mrm_t x86_mrm(x86_reg base,x86_reg index)
{
	return x86_mrm(base,index,sib_scale_1,0);
}

//NEEDS WORK
x86_mrm_t x86_mrm(x86_reg base,x86_reg index,x86_sib_scale scale,x86_ptr disp)
{
	x86_mrm_t rv;
	rv.flags=0;

#ifdef X64
	if (base!=NO_REG && base>EDI)
	{
		rv.flags|=((base>>3)&1)<<2;
		base=(x86_reg)(base&7);
	}
	if (index!=NO_REG && index>EDI)
	{
		rv.flags|=((index>>3)&1)<<3;
		index=(x86_reg)(index&7);
	}
#endif

	verify(index!=ESP);//cant be used

	if(index==NO_REG)
	{
		//no index , ignore scale
		if (base==ESP)
		{
			//special encoding
			//encoded as [none*x + ESP]
			//index ,scale [sib]
			rv.modrm = make_modrm(0,ESP); //ESP means sib
			rv.flags|=1;


			rv.sib=make_sib(0,ESP,base); //none*1+ESP

			if ( disp.ptr_int!=0 )
				rv.modrm |= EncodeDisp(disp.ptr_int,&rv,3);
		}
		else if (base == EBP)
		{
			//special encoding
			//verify(false);
			rv.modrm = make_modrm(0,base);
			//uses [EBP+S8] , or [EBP+S32] forms
			rv.modrm |= EncodeDisp(disp.ptr_int,&rv,3); //32 or 8 bit disp
		}
		else if (base == NO_REG)
		{
			//[disp32]
			//special encoding , will use mode [EBP] (it means disp :p)
			rv.modrm=make_modrm(0,EBP);
			EncodeDisp(disp.ptr_int,&rv,2|4); //only 32b disp allowed , uses form 0
		}
		else
		{
			//[reg] , [reg+disp8/32]
			rv.modrm = make_modrm(0,base);
			if (disp.ptr_int!=0)
				rv.modrm |= EncodeDisp(disp.ptr_int,&rv,3); //32 or 16 bit disp
		}
	}
	else
	{
		//index ,scale [sib]
		rv.modrm = make_modrm(0,ESP);//ESP means sib
		rv.flags|=1;

		bool force_disp=false;
		u8 disp_sz=3;

		
		if (base==EBP)
			force_disp=true;
		
		if (base==NO_REG)
		{
			rv.sib=make_sib(scale,index,EBP);
			disp_sz=2|4; //only 32b disp , return 0 on mrm type
			force_disp=true;
		}
		else
			rv.sib=make_sib(scale,index,base);

		if ( force_disp  || (disp.ptr_int!=0) )
			rv.modrm |= EncodeDisp(disp.ptr_int,&rv,disp_sz);
	}
	return rv;
}
