#include <iostream>
#include <cstdlib>
#include <list>
#include <cstring>
#include "bfstream.h"

using namespace std;

inline
int
System(const string & cmd)
{
   int ret = system(cmd.c_str());
   return ret;
}

class CodeGen {
public:
   void write_instruction(const string & opcode,
                          const string & arg1,
                          const string & arg2, 
                          ofstream & out) {
      if(arg1.size() && arg2.size())
         out << "\t" << opcode << "\t" << arg1 << "," << arg2 << "\n";
      else {
         if(arg1.size())
            out << "\t" << opcode << "\t" << arg1 << endl;
         else 
            out << "\t" << opcode << endl;
      }
   }

   string get_code_string(list< unsigned char > & code2) {
      char buffer[1024];
      string ret;
      unsigned int temp;
      for(list< unsigned char >::iterator ptr = code2.begin(); ptr != code2.end(); ptr++) {
         temp = *ptr;
         sprintf(buffer, "%d", *(unsigned int*)(&temp));
         ret += string(buffer) + string(",");
      }
      return ret;
   }
   void run() {
      const char * opcodes[] = { 
      "push", "pop", "lea", "mov", "sub", "add", "call", "leave", "ret", 
      "jmp", "jz", "jne", "je", "cmp", "pushad", "popad", "int", "nop", "inc", "dec", "imul", "cli", 
      "in", "clc", "and", "or", "not", "lodsw", "salc", "lldt", "adc", "rol", "loadall", "sbb",
      "popfw", "movsb", "movsw", "iretw", "ror", "aaa", "fmul", "test", "pushfw", "shrd", 
      "fxsave", "xchg", "scasw", "lss", "int3", "btr", "out", "enter", NULL,
      };
      const size_t args[] = { 1, 1, 2, 2, 2, 2, 1, 0, 0, 1, 1, 1, 1, 2, 0, 0, 1, 0, 1, 1, 2, 0, 2, 0,
      2, 2, 1, 0, 0, 1, 2, 2, 0, 2, 0, 0, 0, 0, 2, 0, 1, 2, 0, 2, 1, 2, 0, 2, 0, 2, 2, 2, };
      const char * regs[] = {
      "[bp]", "[sp]", "[ax]", "[dx]", "[bx]", "[di]", "[si]", "[cx]",
      "bp", "sp", "ax", "dx", "bx", "di", "si", "cx", "dh", "ah", "es", "ds", "ss", "cs",
      "[ebp]", "[esp]", "[eax]", "[edx]", "[ebx]", "[edi]", "[esi]", "[ecx]", 
      "ebp", "esp", "eax", "edx", "ebx", "edi", "esi", "ecx",
      "al", "ch", "cl"
      "[bp+0x1234]",
      "[sp+0x1234]",
      "[ax+0x1234]",
      "[dx+0x1234]",
      "[bx+0x1234]",
      "[bp-0x1234]",
      "[sp-0x1234]",
      "[ax-0x1234]",
      "[dx-0x1234]",
      "[bx-0x1234]",
      "0x1234", 
      "0x1234", 
      "0x12", 
      "",
      NULL
      };


      ofstream list_out("code.csv", ios::ate | ios::trunc);

      for(size_t op = 0; opcodes[op] != NULL; op++) {
         for(size_t arg1 = 0; regs[arg1] != NULL; arg1++) {
            for(size_t arg2 = 0; regs[arg2] != NULL; arg2++) {
               ofstream out("test.asm", ios::ate | ios::out | ios::trunc);
               out << "[BITS 16]" << endl;
               write_instruction(opcodes[op], regs[arg1], regs[arg2], out);
               out.close();
               if(System("nasm -f bin test.asm -o test.bin 2> /dev/null") == 0) {
                  bfstream in("test.bin", ios::in | ios::binary);
                  if(in) {
                     unsigned char ch;
                     ch = in.get();
                     list< unsigned char > buffer;
                     while(in) {
                        buffer.push_back(ch);
                        ch = in.get();
                     }
                     in.close();
                     string code_string = get_code_string(buffer);
                     if(args[op] == 2) {
                        if(strcmp(regs[arg1], "") == 0 || strcmp(regs[arg2], "") == 0)
                           continue;
                        cout << opcodes[op] << " " << regs[arg1] << "," << regs[arg2] << endl;
                        list_out << "\"" << opcodes[op] << " " 
                                 << regs[arg1] << "," 
                                 << regs[arg2] << "\",\"" 
                                 << code_string << "\"" << endl; 
                     }
                     else if(args[op] == 1) {
                        if(strcmp(regs[arg1], "") == 0)
                           continue;
                        cout << opcodes[op] << " " << regs[arg1] << endl;
                        list_out << "\"" << opcodes[op] << " " 
                                 << regs[arg1] << "\",\""
                                 << code_string << "\"" << endl; 
                     }
                     else if(args[op] == 0) {
                        cout << opcodes[op] << endl;
                        list_out << "\"" << opcodes[op] << "\",\"" 
                                 << code_string << "\"" << endl;
                     }
                     in.close();
                  }
               }
            }
         }
      }

      list_out.close();
   }
};

int
main(int argc, char ** argv)
{
   CodeGen cg;
   cg.run();
   return 0;
}
