#ifdef __x86_64__

# include <QtGlobal>

#ifdef Q_OS_UNIX

#include "virtual_asm.h"
#include <sys/mman.h>
#include <unistd.h>
#include <pthread.h>

//OSX has MAP_ANON
#ifndef MAP_ANONYMOUS
    #define MAP_ANONYMOUS MAP_ANON
#endif

namespace assembler {

unsigned Processor::maxIntArgs64() { return 6;  }

unsigned Processor::maxFloatArgs64() { return 8;  }

bool Processor::isIntArg64Register(unsigned char number, unsigned char arg) { return number < 6;  }

bool Processor::isFloatArg64Register(unsigned char number, unsigned char arg) { return number < 8;  }

Register Processor::intArg64(unsigned char number, unsigned char arg) {
    switch(number) {
        case 0: return Register(*this, EDI); 
        case 1: return Register(*this, ESI); 
        case 2: return Register(*this, EDX); 
        case 3: return Register(*this, ECX); 
        case 4: return Register(*this, R8); 
        case 5: return Register(*this, R9); 
        default: throw "Integer64 argument index out of bounds"; 
    }
}

Register Processor::floatArg64(unsigned char number, unsigned char arg) {
    switch(number) {
        case 0: return Register(*this, XMM0); 
        case 1: return Register(*this, XMM1); 
        case 2: return Register(*this, XMM2); 
        case 3: return Register(*this, XMM3); 
        case 4: return Register(*this, XMM4); 
        case 5: return Register(*this, XMM5); 
        case 6: return Register(*this, XMM6); 
        case 7: return Register(*this, XMM7); 
        default: throw "Float64 argument index out of bounds"; 
    }
}

Register Processor::intArg64(unsigned char number, unsigned char arg, Register defaultReg) {
    if(isIntArg64Register(number, arg)) return intArg64(number, arg); 
    return defaultReg; 
}

Register Processor::floatArg64(unsigned char number, unsigned char arg, Register defaultReg) {
    if(isFloatArg64Register(number, arg)) return floatArg64(number, arg); 
    return defaultReg; 
}

Register Processor::intReturn64() { return Register(*this, EAX);  }

Register Processor::floatReturn64() { return Register(*this, XMM0);  }

CodePage::CodePage(unsigned int Size, void* requestedStart) : used(0), references(1), final(false) {
    unsigned minPageSize = getMinimumPageSize(); 
    unsigned pages = Size / minPageSize; 

    if(Size % minPageSize != 0) pages += 1; 

    size_t reqptr = (size_t)requestedStart; 
    if(reqptr % minPageSize != 0) reqptr -= (reqptr % minPageSize); 

    page = mmap(
        (void*)reqptr,
        Size,
        PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_ANONYMOUS | MAP_PRIVATE,
        0,
        0); 

    size = pages * minPageSize; 
}

void CodePage::grab() { ++references;  }

void CodePage::drop() { if(--references == 0) delete this;  }

CodePage::~CodePage() { munmap(page, size);  }

void CodePage::finalize() {
    mprotect(page, size, PROT_READ | PROT_EXEC); 
    final = true; 
}

unsigned int CodePage::getMinimumPageSize() { return getpagesize();  }

void CriticalSection::enter() { pthread_mutex_lock((pthread_mutex_t*)pLock);  }

void CriticalSection::leave() { pthread_mutex_unlock((pthread_mutex_t*)pLock);  }

CriticalSection::CriticalSection() {
    pthread_mutex_t* mutex = new pthread_mutex_t(); 
    pthread_mutex_init(mutex, 0); 

    pLock = mutex; 
}
CriticalSection::~CriticalSection() {
    pthread_mutex_t* mutex = (pthread_mutex_t*)pLock; 
    pthread_mutex_destroy(mutex); 
    delete mutex; 
}

}; 
#endif // Q_OS_UNIX
#endif // __x86_64__
