#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include "jit.h"

void *malloc_exec(size_t size);
void free_exec(void *ptr);

int ExecJIT_M(ContextM *m, MJIT *jit, int *err)
{
   typedef void (*EXEC)();
   EXEC x = (EXEC)jit->ndata;
   m->cip = m->bytecode;
   m->err = 0;
   x();
   if (err)
      *err = m->err;
   return m->regs[0];
}

void RunJIT_M(ContextM *m, MJIT *jit)
{
   unsigned char *cip;
   unsigned char *ncip;
   unsigned char op;
   unsigned long call_diff;
   unsigned long end_val;
   unsigned long jump_diff;
   size_t codesize = 0;
   size_t calls = 1;

   /* do a sample browse of the machine code */
   cip = m->bytecode; 
   while (*cip != OP_END)
   {
      if (*cip++ == OP_SET)
        cip += 2;
      calls++;
   }

   /* 1 byte for prologue, 1 byte for epilogue */
   codesize = 3;
   /* 6 bytes for our MOV EBP, <addr of M> */
   codesize += 5;
   /* each call will be:
    * 1 byte for push ebp
    * 5 bytes for call
    * 6 bytes for add esp
    * 7 bytes for add 
    * 7 bytes for cmp
    * 6 bytes for dword conditional jump
    */
   codesize += calls * (7+6+7+6+5+1);
   ncip = malloc_exec(codesize);
   /* 1 byte from end of function */
   end_val = (unsigned long)ncip + codesize - 2;
   jit->code_size = codesize;
   jit->ndata = ncip;
   *ncip++ = 0x55;	/* push ebp */
   *ncip++ = 0xBD;	/* MOV EBP, */
   *(long *)ncip = (long)m;	/* m */
   ncip += sizeof(long);
   cip = m->bytecode;
   /* begin code generation */
   do
   {
      op = *cip++;
      if (op == OP_SET)
         cip += 2;
      *ncip++ = 0x55;	/* push ebp */
      *ncip++ = 0x81;	/* add */
      *ncip++ = 0x45;	/* ebp+ */
      *ncip++ = offsetof(ContextM, cip); /* m->cip */
      *(int *)ncip = 0x01;	/* 1 */
      ncip += sizeof(int);
      *ncip++ = 0xE8;	/* eip rel call */
      call_diff = (unsigned long)m->oplist[op] - (unsigned long)(ncip + 4);
      *(unsigned long *)ncip = call_diff;
      ncip += sizeof(long);
      *ncip++ = 0x81;	/* add */
      *ncip++ = 0xC4;	/* esp */
      *(int *)ncip = 0x04; /* 4 */
      ncip += sizeof(int);
      *ncip++ = 0x81;	/* cmp */
      *ncip++ = 0x7D;	/* ebp+ */
      *ncip++ = offsetof(ContextM, err); /* m->err */
      *(int *)ncip = 0x00; /* 0 */
      ncip += sizeof(int);
      *ncip++ = 0x0F;	/* jump */
      *ncip++ = 0x85;	/* ne */
      jump_diff = end_val - ((unsigned long)ncip + 4);
      *(unsigned long *)ncip = jump_diff;
      ncip += sizeof(long);
   } while (op != OP_END);

   *ncip++ = 0x5D;	/* pop ebp */
   *ncip++ = 0xC3;	/* ret */
}

void FreeJIT_M(MJIT *jit)
{
   free(jit->ndata);
}


#if defined __WIN32__
#include <windows.h>

void *malloc_exec(size_t size)
{
   return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
}

void free_exec(void *ptr)
{
   VirtualFree(ptr, 0, MEM_RELEASE);
}

#elif defined __linux__
#include <sys/mman.h>
void *malloc_exec(size_t size)
{
   void *ptr = malloc(size);
   if (ptr)
      mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC);
   return ptr;
}

#define free_exec(ptr)	free(ptr)
#endif

