#include<iostream.h>
#include<fstream.h>
#include<conio.h>
#include<string.h>
#include<stdlib.h>
#include<values.h>

enum sub_instr_type{
immediate_to_reg=1,

undef_sub_instr_type
};

enum instr_type{
e_mov=1,
e_inter,
e_rets,

undef_instr_type
};

enum instr_operands{
w0_13b_al=0,w0_13b_cl=1,w0_13b_dl=2,w0_13b_bl=3,
w0_13b_ah=4,w0_13b_ch=5,w0_13b_dh=6,w0_13b_bh=7,

w1_16b_ax=0,w1_16b_cx=1,w1_16b_dx=2,w1_16b_bx=3,
w1_16b_sp=4,w1_16b_bp=5,w1_16b_si=6,w1_16b_di=7,

w1_32b_eax=0,w1_32b_ecx=1,w1_32b_edx=2,w1_32b_ebx=3,
w1_32b_esp=4,w1_32b_ebp=5,w1_32b_esi=6,w1_32b_edi=7,

immediate_data=8,undef_instr_operands
};

struct instr_layout{
unsigned int opcode1:8;
unsigned int opcode2:8;

unsigned int mod:2;
unsigned int reg:3;
unsigned int r_m:3;

unsigned int scale:2;
unsigned int index:3;
unsigned int base:3;

unsigned long int displacement;
unsigned long int immediate_data;

//aditional fields encoded
unsigned int d:1;
unsigned int w:1;
unsigned int s:1;
unsigned int tttn:4;
unsigned int sreg2:2;
unsigned int sreg3:3;
unsigned int eee:3;
};

class x86Instruction{
private:
  instr_layout RInstr;
  instr_type RInstrType;
  sub_instr_type RSubInstrType;
  int currComLine;
  char *formatedCommand;

public:
  char commandLength;
  void procRawCom(char *com);
  void chargeOpcodes();
  instr_type getInstrType(char *com);
  instr_operands getRegister(char *reg);
  void getOperands(char *com);
  char* encodeInstr();
};


void x86Instruction::chargeOpcodes()
{
switch(RInstrType)
 {
 case e_inter:
    RInstr.opcode1=205;//interrupt type
    RInstr.opcode2=RInstr.immediate_data;

    formatedCommand[0]=RInstr.opcode1;
    formatedCommand[1]=RInstr.opcode2;
    commandLength=2;
    formatedCommand[2]='\0';
  break;
 case e_mov: //move instruction is present
  switch(RSubInstrType)
   {
   case immediate_to_reg:
    RInstr.opcode1=22;
    RInstr.opcode1|=RInstr.w;
    RInstr.opcode1<<=3;
    RInstr.opcode1|=RInstr.reg;

    formatedCommand[0]=RInstr.opcode1;
    formatedCommand[1]=(char)RInstr.immediate_data;
    formatedCommand[2]='\0';
    commandLength=2;
    break;
   }
  break;
  case e_rets: //return from proc to same segment
  RInstr.opcode1=195;

  formatedCommand[0]=RInstr.opcode1;
  formatedCommand[1]='\0';
  commandLength=1;
  break;
 }
}

instr_operands x86Instruction::getRegister(char *reg)
{

//1(6)3(2) w=0 registers
if(strcmp(reg,"al")==0) {RInstr.w=0;return w0_13b_al;}
if(strcmp(reg,"cl")==0) {RInstr.w=0;return w0_13b_cl;}
if(strcmp(reg,"dl")==0) {RInstr.w=0;return w0_13b_dl;}
if(strcmp(reg,"bl")==0) {RInstr.w=0;return w0_13b_bl;}
if(strcmp(reg,"ah")==0) {RInstr.w=0;return w0_13b_ah;}
if(strcmp(reg,"ch")==0) {RInstr.w=0;return w0_13b_ch;}
if(strcmp(reg,"dh")==0) {RInstr.w=0;return w0_13b_dh;}
if(strcmp(reg,"bh")==0) {RInstr.w=0;return w0_13b_bh;}

//16 w=1 registers
if(strcmp(reg,"ax")==0) {RInstr.w=1;return w1_16b_ax;}
if(strcmp(reg,"cx")==0) {RInstr.w=1;return w1_16b_cx;}
if(strcmp(reg,"dx")==0) {RInstr.w=1;return w1_16b_dx;}
if(strcmp(reg,"bx")==0) {RInstr.w=1;return w1_16b_bx;}
if(strcmp(reg,"sp")==0) {RInstr.w=1;return w1_16b_sp;}
if(strcmp(reg,"bp")==0) {RInstr.w=1;return w1_16b_bp;}
if(strcmp(reg,"si")==0) {RInstr.w=1;return w1_16b_si;}
if(strcmp(reg,"di")==0) {RInstr.w=1;return w1_16b_di;}

//32 w=1 registers
if(strcmp(reg,"eax")==0) {RInstr.w=1;return w1_32b_eax;}
if(strcmp(reg,"ecx")==0) {RInstr.w=1;return w1_32b_ecx;}
if(strcmp(reg,"edx")==0) {RInstr.w=1;return w1_32b_edx;}
if(strcmp(reg,"ebx")==0) {RInstr.w=1;return w1_32b_ebx;}
if(strcmp(reg,"esp")==0) {RInstr.w=1;return w1_32b_esp;}
if(strcmp(reg,"ebp")==0) {RInstr.w=1;return w1_32b_ebp;}
if(strcmp(reg,"esi")==0) {RInstr.w=1;return w1_32b_esi;}
if(strcmp(reg,"edi")==0) {RInstr.w=1;return w1_32b_edi;}

return undef_instr_operands;
}

instr_type x86Instruction::getInstrType(char *com)
{
if(strcmp(com,"move")==0) return e_mov;
if(strcmp(com,"inter")==0) return e_inter;
if(strcmp(com,"rets")==0) return e_rets;

//command not suported..abort!
cprintf("*** (line %d) Command %s is not suported!",currComLine,com);
getch();
exit(1);

return undef_instr_type;
}

void x86Instruction::procRawCom(char *com)
{
  char com_sep[50];
  strcpy(com_sep,com);
 //see if comand is more then a standalone mnemonic
 if(strchr(com,' ')!=NULL)
 {
 strncpy(com_sep,com,strchr(com,' ')-com);  //aquire command
 com_sep[strchr(com,' ')-com]='\0'; //???
 RInstrType=getInstrType(com_sep);

 getOperands(com);
 }
 else //instr without param present
  RInstrType=getInstrType(com_sep);

 chargeOpcodes();
}

void x86Instruction::getOperands(char *com)
{
 if(strchr(com,' ')==NULL)
  {
  cprintf(">*< x86Instruction.getRegisters 'if' failed!");
  getch();
  exit(1);
  }

  char firstOp[20],secondOp[20];

  com=strchr(com,' ')+1;
  com=strlwr(com);

  strcpy(firstOp,com);
  if(strchr(com,' '))
  firstOp[strchr(com,' ')-com]='\0';

  //for 2 data instructions
  if(strstr(com,"to")!=NULL||
     strstr(com,"from")!=NULL){

  strcpy(secondOp,strrchr(com,' ')+1);
  RInstr.reg=getRegister(secondOp);
  }

  if(strcmp(firstOp,"0")==0||
     atoi(firstOp)!=0){
  RInstr.immediate_data=atoi(firstOp);

  //notice that it is an "immediate to reg request"
  RSubInstrType=immediate_to_reg;
  }
}

char* x86Instruction::encodeInstr()
{
return formatedCommand;
}


void aquireRawCommand(char *com)
{
if(strlen(com)!=0){
int i=0;
while(com[i]==' ') i++;
com+=i;

if(strchr(com,'*')==NULL){} //???
 else
com[strchr(com,'*')-com]='\0';

if(strchr(com,',')==NULL){} //???
com[strchr(com,',')-com]='\0';
}

}

int main(int argc,char *argv[])
{
clrscr();
x86Instruction currInstr;
int i;
char *currLine;

if(argc>2){
cout<<"<***>Building source code "<<argv[1]<<" !";
cout<<endl<<"eAsm code assembly report:\n";

ifstream f(argv[1]);
ofstream g(argv[2]);
while(!f.eof())
 {
 f.getline(currLine,2000);
 aquireRawCommand(currLine);

 if(strlen(currLine)!=0){
 currInstr.procRawCom(currLine);
 cout<<endl<<currLine<<" : ";
 for(i=0;i<currInstr.commandLength;i++)
  {
   cout<<(int)(unsigned char)currInstr.encodeInstr()[i]<<" ";
   g<<currInstr.encodeInstr()[i];
  }
 }
 }
 cout<<"\n\n<***>Program built :"<<argv[2]<<" !";
 cout<<"\n     EniAsm v0.2 by 3Nigma[eni4ever.com]";
 f.close();
 g.close();
 getch();
}
return 0;
}
