# include <QtGlobal>

#ifdef Q_PROCESSOR_X86_64

#include "virtual_asm.h"
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stack>
#include <stdio.h>
#include <unordered_set>

//See http://ref.x86asm.net/coder32.html for a list of x86 opcode bytes

namespace assembler {

enum OpExtension : byte {
    EX_0 = 0,
    EX_1 = 1,
    EX_2 = 2,
    EX_3 = 3,
    EX_4 = 4,
    EX_5 = 5,
    EX_6 = 6,
    EX_7 = 7,
}; 

enum ModBits : byte {
    ADR = 0,
    ADR8 = 1,
    ADR32 = 2,
    REG = 3
}; 

enum IndexScale : byte {
    Scale1 = 0,
    Scale2 = 1,
    Scale4 = 2,
    Scale8 = 3
}; 

unsigned char mod_rm(unsigned char Reg, unsigned char Mod, unsigned char RM) {
    return (Mod<<6) | (Reg<<3) | RM; 
}

unsigned char sib(unsigned char ScaledIndex, unsigned char Mode, unsigned char BaseReg) {
    return (Mode<<6) | (ScaledIndex<<3) | BaseReg; 
}

struct Argument {
    Register* reg; 
    MemAddress* mem; 
    uint64_t constant; 
    bool is32; 
    Argument(Register* r) : reg(r), mem(0) {}
    Argument(MemAddress* m) : reg(0), mem(m) {}
    Argument(unsigned Constant32) : reg(0), mem(0), constant(Constant32), is32(true) {}
    Argument(uint64_t Constant) : reg(0), mem(0), constant(Constant), is32(false) {}
}; 

void moveToUpperDWORD(MemAddress& address) {
    if(address.absolute_address == 0)
        address.offset += 4; 
    else
        address.absolute_address = (byte*)address.absolute_address + 4; 
}

AddrPrefix MemAddress::prefix(unsigned char further, bool defLong) {
    return AddrPrefix(*this, defLong, further); 
}

template<>
MemAddress as<float>(MemAddress addr) {
    addr.bitMode = sizeof(float) * 8; 
    addr.Signed = true; 
    addr.Float = true; 
    return addr; 
}

template<>
MemAddress as<double>(MemAddress addr) {
    addr.bitMode = sizeof(double) * 8; 
    addr.Signed = true; 
    addr.Float = true; 
    return addr; 
}

unsigned Processor::pushSize() {
    return sizeof(void*); 
}

void Processor::call_cdecl_prep(unsigned argBytes) {
    unsigned stackOffset = (stackDepth + argBytes) % 16; 
    Register esp(*this, ESP, sizeof(void*) * 8); 
    if(stackOffset != 0)
        esp -= 16 - stackOffset; 
}

void Processor::call_cdecl_end(unsigned argBytes, bool returnPointer) {
    Register esp(*this, ESP, sizeof(void*) * 8); 
    unsigned stackOffset = (stackDepth + argBytes) % 16; 
    if(stackOffset != 0)
        argBytes += (16 - stackOffset); 
#ifndef _MSC_VER
    if(returnPointer)
        argBytes -= 4; 
#endif
    if(argBytes != 0)
        esp += argBytes; 
}

unsigned Processor::call_cdecl_args(const char* args, va_list ap) {
    return call_thiscall_args(0, args, ap); 
}

unsigned Processor::call_thiscall_args(Register* obj, const char* args, va_list ap) {
    std::stack<Argument> arg_stack; 
    std::unordered_set<unsigned char> used_regs; 
    Register esp(*this, ESP, sizeof(void*) * 8); 

    unsigned argCount = 0, floatCount = 0, intCount = 0, regCount = 0, stackBytes = 0; 

    //Set the object as first argument
    if(obj) {
        if(!isIntArg64Register(intCount, argCount))
            stackBytes += pushSize(); 
        ++argCount; 
        ++intCount; 
        arg_stack.push(obj); 
    }

    //Read the arguments in...
    while(args && *args != '\0') {
        if(*args == 'r') {
            Register* reg = va_arg(ap,Register*); 
            if(reg->xmm()) {
                if(!isFloatArg64Register(floatCount, argCount))
                    stackBytes += pushSize(); 
                ++floatCount; 
            }
            else {
                if(!isIntArg64Register(intCount, argCount))
                    stackBytes += pushSize(); 
                ++intCount; 
            }
            ++regCount; 
            arg_stack.push(reg); 
        }
        else if(*args == 'm') {
            MemAddress* adr = va_arg(ap,MemAddress*); 
            if(adr->Float) {
                if(!isFloatArg64Register(floatCount, argCount))
                    stackBytes += pushSize(); 
                ++floatCount; 
            }
            else {
                if(!isIntArg64Register(intCount, argCount))
                    stackBytes += pushSize(); 
                ++intCount; 
            }
            arg_stack.push(adr); 
        }
        else if(*args == 'p') {
            if(!isIntArg64Register(intCount, argCount))
                stackBytes += pushSize(); 
            arg_stack.push(va_arg(ap,uint64_t)); 
            ++intCount; 
        }
        else if(*args == 'c') {
            if(!isIntArg64Register(intCount, argCount))
                stackBytes += pushSize(); 
            arg_stack.push(va_arg(ap,unsigned)); 
            ++intCount; 
        }
        else
            throw 0; 
        ++argCount; 
        ++args; 
    }

#ifdef _MSC_VER
    if(stackBytes < 32)
        stackBytes = 32; 
#endif

    call_cdecl_prep(stackBytes); 

    unsigned intA = intCount - 1, floatA = floatCount - 1, a = argCount - 1; 

    //Then push them in reverse order
    while(!arg_stack.empty()) {
        auto& arg = arg_stack.top(); 

        if(arg.reg) {
            Register reg = *arg.reg; 

            if(reg.code == ESP || reg.code == R15)
                throw "Cannot use this register for cdecl call wrapper."; 

            if(reg.xmm()) {
                if(isFloatArg64Register(floatA, a)) {
                    Register other = floatArg64(floatA, a); 
                    other.bitMode = reg.bitMode; 

                    if(other.code != reg.code) {
                        if(used_regs.find(reg.code) != used_regs.end())
                            throw "Invalid out-of-order use of argument register."; 
                        other = reg; 
                    }

                    used_regs.insert(other.code); 
                }
                else {
                    if(used_regs.find(reg.code) != used_regs.end())
                        throw "Invalid out-of-order use of argument register."; 
                    esp -= pushSize(); 
                    MemAddress adr = *esp; 
                    adr.bitMode = reg.bitMode; 

                    adr = reg; 
                }
                --floatA; 
            }
            else {
                if(isIntArg64Register(intA, a)) {
                    Register other = intArg64(intA, a); 
                    other.bitMode = reg.bitMode; 

                    if(other.code != reg.code) {
                        if(used_regs.find(reg.code) != used_regs.end())
                            throw "Invalid out-of-order use of argument register."; 
                        other = reg; 
                    }

                    used_regs.insert(other.code); 
                }
                else {
                    if(used_regs.find(reg.code) != used_regs.end())
                        throw "Invalid out-of-order use of argument register."; 
                    push(reg); 
                }
                --intA; 
            }
        }
        else if(arg.mem) {
            MemAddress addr = *arg.mem; 

            if(argCount > 1) {
                for(unsigned i = 0;  i < maxIntArgs64();  ++i) {
                    if(addr.code == intArg64(i, i).code)
                        throw "Cannot use this register for cdecl call wrapper address."; 
                    if(addr.other == intArg64(i, i).code)
                        throw "Cannot use this register for cdecl call wrapper address."; 
                    if(addr.scaleFactor != 0 && addr.scaleReg == intArg64(i, i).code)
                        throw "Cannot use this register for cdecl call wrapper address."; 
                }
            }
            if(addr.code == R15 || addr.other == R15 || (addr.scaleFactor != 0 && addr.scaleReg == R15))
                throw "Cannot use this register for cdecl call wrapper address."; 
            if(addr.other == ESP)
                throw "Cannot use this register for cdecl call wrapper address."; 
            if(addr.code == ESP) {
                addr.code = R15; 
                addr.offset += pushSize(); 
            }

            if(addr.Float) {
                if(isFloatArg64Register(floatA, a)) {
                    Register reg = floatArg64(floatA, a); 
                    reg.bitMode = addr.bitMode; 

                    reg = addr; 
                }
                else {
                    push(addr); 
                }
                --floatA; 
            }
            else {
                if(isIntArg64Register(intA, a)) {
                    Register reg = intArg64(intA, a); 
                    reg.bitMode = addr.bitMode; 

                    reg = addr; 
                }
                else {
                    push(addr); 
                }
                --intA; 
            }
        }
        else {
            if(isIntArg64Register(intA, a)) {
                Register reg = intArg64(intA, a); 
                if(arg.is32)
                    reg.bitMode = 32; 
                else
                    reg.bitMode = 64; 
                reg = arg.constant; 
            }
            else {
                push(arg.constant); 
            }
            --intA; 
        }


        arg_stack.pop(); 
        --a; 
    }

#ifdef _MSC_VER
    esp -= 32; 
#endif

    return stackBytes; 
}

void Processor::call_cdecl(void* func, const char* args, va_list ap) {
    unsigned stackBytes = call_cdecl_args(args, ap); 
    call(func); 
    call_cdecl_end(stackBytes); 
}

unsigned Processor::call_cdecl_args(const char* args, ...) {
    va_list ap; 
    va_start(ap, args); 
        unsigned r = call_cdecl_args(args, ap); 
    va_end(ap); 
    return r; 
}

void Processor::call_cdecl(void* func, const char* args, ...) {
    va_list ap; 
    va_start(ap, args); 
        call_cdecl(func, args, ap); 
    va_end(ap); 
}

unsigned Processor::call_thiscall_args(Register* obj, const char* args, ...) {
    va_list ap; 
    va_start(ap, args); 
        unsigned r = call_thiscall_args(obj, args, ap); 
    va_end(ap); 
    return r; 
}

void Processor::call_stdcall(void* func, const char* args, ...) {
    va_list ap; 
    va_start(ap, args); 
        call_cdecl(func, args, ap); 
    va_end(ap); 
}

Processor::Processor(CodePage& codePage, unsigned defaultBitMode ) {
    op = codePage.getActivePage(); 
    pageStart = op; 
    bitMode = defaultBitMode; 
    lastBitMode = bitMode; 
    stackDepth = pushSize(); 
    jumpSpace = 0; 
}

void Processor::migrate(CodePage& prevPage, CodePage& newPage) {
    jump(Jump,newPage.getActivePage()); 
    jumpPtr = op; 
    op += jumpSpace; 
    prevPage.markUsedAddress((void*)op); 
    op = newPage.getActivePage(); 
    pageStart = op; 
    jumpSpace = 0; 
}

template<>
Processor& Processor::operator<<(MemAddress addr) {
    if(addr.absolute_address != 0) {
        if((size_t)addr.absolute_address <= INT_MAX) {
            //Normal 32 bit address needs a none sib byte
            return *this << mod_rm(addr.other % 8, ADR, SIB) << sib(0x4, 0, ADDR) << (int)(size_t)addr.absolute_address; 
        }
        else {
            //Take 64 bit absolute address from R11
            return *this << mod_rm(addr.other % 8, ADR, R11 % 8); 
        }
    }

    if(addr.scaleFactor != 0) {
        IndexScale scale; 
        switch(addr.scaleFactor) {
        case 1:
            scale = Scale1;  break; 
        case 2:
            scale = Scale2;  break; 
        case 4:
            scale = Scale4;  break; 
        case 8:
            scale = Scale8;  break; 
        default: throw 0; 
        }

        //Can't scale with an offset from ESP
        if(addr.scaleReg % 8 == ESP)
            throw 0; 

        //EBP/R13 doesn't have an ADR mode, so use ADR8 and append a 0
        if(addr.offset == 0 && addr.code % 8 != EBP)
            return *this << mod_rm(addr.other % 8, ADR, SIB) << sib(addr.scaleReg % 8, scale, addr.code % 8); 
        else if(addr.offset >= CHAR_MIN && addr.offset <= CHAR_MAX)
            return *this << mod_rm(addr.other % 8, ADR8, SIB) << sib(addr.scaleReg % 8, scale, addr.code % 8) << (char)addr.offset; 
        else
            return *this << mod_rm(addr.other % 8, ADR32, SIB) << sib(addr.scaleReg % 8, scale, addr.code % 8) << addr.offset; 
    }

    if(addr.offset == 0 && addr.code % 8 != EBP) { //[EBP] means absolute address, so we use [EBP+0] instead
        *this << mod_rm(addr.other % 8, ADR, addr.code % 8); 
        if(addr.code % 8 == ESP)
            *this << '\x24';  //SIB byte indicating [ESP]
    }
    else if(addr.offset >= CHAR_MIN && addr.offset <= CHAR_MAX) {
        *this << mod_rm(addr.other % 8, ADR8, addr.code % 8); 
        if(addr.code % 8 == ESP)
            *this << '\x24';  //SIB byte indicating [ESP]
        *this << (char)addr.offset; 
    }
    else {
        *this << mod_rm(addr.other % 8, ADR32, addr.code % 8); 
        if(addr.code % 8 == ESP)
            *this << '\x24';  //SIB byte indicating [ESP]
        *this << addr.offset; 
    }
    return *this; 
}

template<>
Processor& Processor::operator<<(AddrPrefix pr) {
    MemAddress& adr = pr.adr; 

    //64 bit absolute addresses need to mangle a register in order to work
    if((size_t)adr.absolute_address > (size_t)INT_MAX) {
        *this << '\x49' << '\xBB' << (uint64_t)adr.absolute_address; 
        adr.code = R11; 
    }

    //Further prefixes that need to be before the REX byte
    if(pr.further != 0)
        *this << pr.further; 

    //64 bit prefix
    unsigned char ch = '\x40'; 
    if(!pr.defLong) {
        if(adr.bitMode == 64)
            ch |= '\x08'; 
    }
    if((adr.code % 16) > 7)
        ch |= '\x01'; 
    if((adr.other % 16) > 7)
        ch |= '\x04'; 
    if(adr.scaleReg > 7)
        ch |= '\x02'; 
    if(ch != '\x40')
        *this << ch; 
    return *this; 
}

template<>
Processor& Processor::operator<<(RegPrefix pr) {
    Register& reg = pr.reg; 
    unsigned short other = pr.other; 

    unsigned char ch = '\x40'; 
    if(!pr.defLong) {
        if(reg.getBitMode() == 64)
            ch |= '\x08'; 
    }
    if((reg.code % 16) > 7)
        ch |= '\x01'; 
    if((other % 16) > 7)
        ch |= '\x04'; 

    if(ch != '\x40')
        *this << ch; 
    return *this; 
}

void Processor::push(Register& reg) {
    *this << reg.prefix(EX_0, true) << byte(0x50u+(reg.code % 8)); 
}

void Processor::pop(Register& reg) {
    *this << reg.prefix(EX_0, true) << byte(0x58u+(reg.code % 8)); 
}

void Processor::push(MemAddress address) {
    address.other = EX_6; 
    *this << address.prefix() << '\xFF' << address; 
}

void Processor::pop(MemAddress address) {
    address.other = EX_0; 
    *this << address.prefix() << '\x8F' << address; 
}

void Processor::push(size_t value) {
    if(value <= CHAR_MAX)
        *this << '\x6A' << (byte)value; 
    else
        *this << '\x48' << '\x68' << (long long)value; 
}

void Processor::pop(unsigned int count) {
    Register esp(*this, ESP); 
    esp += count * pushSize(); 
}

void Processor::call(Register& reg) {
    *this << reg.prefix() << '\xFF' << reg.modrm(EX_2); 
}

void Processor::call(void* func) {
    int64_t offset = ((byte*)func - op) - 5; 
    if(offset < (int64_t)INT_MIN || offset > (int64_t)INT_MAX) {
        uint64_t abs = (uint64_t)func; 
        if(abs > (uint64_t)UINT_MAX)
            *this << '\x49' << '\xBB' << abs; 
        else //0-extend small actual addresses
            *this << '\x41' << '\xBB' << (unsigned)abs; 
        *this << '\x49' << '\xFF' << mod_rm(EX_2,REG,R11 % 8); 
    }
    else {
        *this << '\xE8' << (int)offset; 
    }
}

unsigned char shortJumpCodes[JumpTypeCount] = { 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0xEB}; 
unsigned char longJumpCodes[JumpTypeCount] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0xE9 }; 

void* Processor::prep_short_jump(JumpType type) {
    *this << shortJumpCodes[type]; 
    void* ret = (void*)op; 
    *this << (char)0; 
    return ret; 
}

void Processor::end_short_jump(void* p) {
    volatile byte* jumpFrom = (volatile byte*)p; 
    int64_t offset = ((size_t)op - (size_t)jumpFrom) - 1; 
    if(offset < CHAR_MIN || offset > CHAR_MAX)
        throw "Short jump too long."; 
    *jumpFrom = (char)offset; 
}

void* Processor::prep_long_jump(JumpType type) {
    if(type != Jump)
        *this << '\x0F'; 
    *this << longJumpCodes[type]; 
    void* ret = (void*)op; 
    *this << (int)0; 
    jumpSpace += 16; 
    return ret; 
}

void Processor::end_long_jump(void* p) {
    volatile byte* jumpFrom = (volatile byte*)p; 
    bool isSamePage = (size_t)jumpFrom >= (size_t)pageStart && (size_t)jumpFrom < (size_t)op; 
    int64_t offset = ((size_t)op - (size_t)jumpFrom) - 4; 
    if(offset < (int64_t)INT_MIN || offset > (int64_t)INT_MAX) {
        if(isSamePage)
            throw "Inside-page long jump too long. This should never ever happen, somebody screwed the pooch."; 
        Register reg(*this, R11, sizeof(void*)); 
        auto* prevOp = op; 
        op = jumpPtr; 

        reg = (void*)prevOp; 
        *this << reg.prefix(EX_4) << '\xFF' << reg.modrm(EX_4); 

        offset = ((size_t)jumpPtr - (size_t)jumpFrom) - 4; 
        if(offset < (int64_t)INT_MIN || offset > (int64_t)INT_MAX)
            throw "Multi-page drifting! Can't recover from this."; 
        *(volatile int*)jumpFrom = (int)offset; 

        jumpPtr = op; 
        op = prevOp; 
        return; 
    }
    *(volatile int*)jumpFrom = (int)offset; 
    if(isSamePage)
        jumpSpace -= 16; 
}

void Processor::jump(JumpType type, volatile byte* dest) {
    int64_t offset = ((size_t)dest - (size_t)op) - 2; 
    if(offset >= CHAR_MIN && offset <= CHAR_MAX)
        *this << shortJumpCodes[type] << (char)offset; 
    else if (offset > INT_MAX || offset < INT_MIN) {
        Register reg(*this, R11, sizeof(void*)); 
        reg = (void*)dest; 
        jump(reg); 
    }
    else if(type == Jump)
        *this << longJumpCodes[Jump] << (int)(offset-3);  //Long jump is 3 bytes larger, jump is from the end of the full opcode
    else
        *this << '\x0F' << longJumpCodes[type] << (int)(offset-4);  //Conditional long jump is 4 bytes larger, jump is from the end of the full opcode
}

void Processor::jump(Register& reg) {
    *this << reg.prefix(EX_4) << '\xFF' << reg.modrm(EX_4); 
}

void Processor::loop(volatile byte* dest, JumpType type) {
    int64_t off = dest - op - 2; 
    if(off < CHAR_MIN || off > CHAR_MAX)
        throw "Loop offset too far."; 

    if(type == Jump)
        *this << '\xE2' << (char)off; 
    else if(type == Zero)
        *this << '\xE1' << (char)off; 
    else if(type == NotZero)
        *this << '\xE0' << (char)off; 
    else
        throw "Unsupported loop type."; 
}

void Processor::string_copy(unsigned size) {
    if(size == 1)
        *this << '\xA4'; 
    else if(size == 2)
        *this << '\x66' << '\xA5'; 
    else if(size == 4)
        *this << '\xA5'; 
    else if(size == 8)
        *this << '\x48' << '\xA5'; 
    else
        throw "Invalid string copy step size."; 
}

void Processor::setDirFlag(bool forward) {
    if(forward)
        *this << '\xFC'; 
    else
        *this << '\xFD'; 
}

void Processor::ret() {
    *this << '\xC3'; 
}

void Processor::debug_interrupt() {
    *this << '\xCC'; 
}

MemAddress::MemAddress(Processor& CPU, void* address)
    : cpu(CPU), code(ESP), absolute_address(address), other(NONE),
    offset(0), bitMode(cpu.bitMode), Float(false), Signed(false), scaleFactor(0), scaleReg(NONE) {}

MemAddress::MemAddress(Processor& CPU, RegCode Code)
    : cpu(CPU), code(Code), absolute_address(0), other(NONE),
    offset(0), bitMode(cpu.bitMode), Float(false), Signed(false), scaleFactor(0), scaleReg(NONE) {}

MemAddress::MemAddress(Processor& CPU, RegCode Code, int Offset)
    : cpu(CPU), code(Code), absolute_address(0), other(NONE),
    offset(Offset), bitMode(cpu.bitMode), Float(false), Signed(false), scaleFactor(0), scaleReg(NONE) {}

MemAddress MemAddress::operator+(ScaledIndex scale) {
    scaleReg = scale.reg; 
    scaleFactor = scale.scaleFactor; 
    return *this; 
}

MemAddress MemAddress::operator+(int Offset) {
    offset += Offset; 
    return *this; 
}

MemAddress MemAddress::operator-(int Offset) {
    offset -= Offset; 
    return *this; 
}

void MemAddress::operator++() {
    switch(bitMode) {
    case 8:
        cpu << prefix() << '\xFE' << *this;  break; 
    case 16:
        cpu << prefix('\x66') << '\xFF' << *this;  break; 
    default:
        cpu << prefix() << '\xFF' << *this;  break; 
    }
}

void MemAddress::operator--() {
    other = EX_1; 
    switch(bitMode) {
    case 8:
        cpu << prefix() << '\xFE' << *this;  break; 
    case 16:
        cpu << prefix('\x66') << '\xFF' << *this;  break; 
    default:
        cpu << prefix() << '\xFF' << *this;  break; 
    }
}

void MemAddress::operator-() {
    other = EX_3; 
    switch(bitMode) {
    case 8:
        cpu << prefix() << '\xF6' << *this;  break; 
    case 16:
        cpu << prefix('\x66') << '\xF7' << *this;  break; 
    default:
        cpu << prefix() << '\xF7' << *this;  break; 
    }
}

void MemAddress::operator~() {
    other = EX_2; 
    switch(bitMode) {
    case 8:
        cpu << prefix() << '\xF6' << *this;  break; 
    case 16:
        cpu << prefix('\x66') << '\xF7' << *this;  break; 
    default:
        cpu << prefix() << '\xF7' << *this;  break; 
    }
}

void MemAddress::operator+=(unsigned int amount) {
    if(amount == 0) return; 

    if(code == EAX)
        cpu << '\x05' << amount; 
    else if(amount <= CHAR_MAX)
        cpu << prefix() << '\x83' << *this << (byte)amount; 
    else
        cpu << prefix() << '\x81' << *this << amount; 
}

void MemAddress::operator-=(unsigned int amount) {
    if(amount == 0) return; 

    other = (RegCode)EX_5; 
    if(code == EAX)
        cpu << '\x2D' << amount; 
    else if(amount <= CHAR_MAX)
        cpu << prefix() << '\x83' << *this << (byte)amount; 
    else
        cpu << prefix() << '\x81' << *this << amount; 
}

void MemAddress::operator=(unsigned int value) {
    switch(bitMode) {
    case 8:
        cpu << prefix() << '\xC6' << *this << (byte)value;  break; 
    case 16:
        cpu << prefix('\x66') << '\xC7' << *this << (unsigned short)value;  break; 
    case 32:
    default:
        cpu << prefix() << '\xC7' << *this << value;  break; 
    }
}

void MemAddress::operator&=(unsigned int value) {
    other = EX_4; 
    cpu << prefix() << '\x81' << *this << value; 
}

void MemAddress::operator|=(unsigned int value) {
    other = EX_7; 
    switch(bitMode) {
    case 8:
        cpu << prefix() << '\x80' << *this << (byte)value;  break; 
    case 16:
        cpu << prefix('\x66') << '\x81' << *this << (unsigned short)value;  break; 
    case 32:
    case 64:
        cpu << prefix() << '\x81' << *this << value;  break; 
    }
}

void MemAddress::operator=(void* value) {
    bitMode = 64; 
    if((size_t)value < (size_t)INT_MAX) {
        cpu << prefix() << '\xC7' << *this << (unsigned)(size_t)value; 
    }
    else {
        if((size_t)absolute_address > UINT_MAX - sizeof(void*) * 8) {
            bitMode = 32; 
            unsigned* values = (unsigned*)&value; 

            cpu << prefix() << '\xC7' << *this << values[0]; 
            moveToUpperDWORD(*this); 
            cpu << prefix() << '\xC7' << *this << values[1]; 
        }
        else {
            other = R11; 
            cpu << '\x49' << '\xBB' << (uint64_t)value; 
            cpu << prefix() << '\x89' << *this; 
        }
    }
}

void MemAddress::operator=(Register fromReg) {
    other = fromReg.code; 
    if(fromReg.xmm()) {
        switch(bitMode) {
            case 32:
                cpu << prefix('\xF3') << '\x0F' << '\x11' << *this; 
            break; 
            case 64:
                cpu << prefix('\xF2', true) << '\x0F' << '\x11' << *this; 
            break; 
            default:
                throw "Unsupported bitmode for xmm register"; 
        }
    }
    else {
        switch(bitMode) {
        case 8:
            cpu << prefix() << '\x88' << *this;  break; 
        case 16:
            cpu << prefix('\x66') << '\x89' << *this;  break; 
        default:
            cpu << prefix() << '\x89' << *this;  break; 
        }
    }
}

void MemAddress::direct_copy(MemAddress address, Register& intermediate) {
    if(bitMode != address.bitMode)
        throw 0; 
    other = intermediate.code;  address.other = other; 
    switch(bitMode) {
    case 8:
    case 16:
    case 32:
        address.bitMode = 32; 

        intermediate = address; 
        *this = intermediate; 
        break; 
    case 64:
        address.bitMode = 64; 

        intermediate = address; 
        *this = intermediate; 
        break; 
    }
}

Register::Register(Processor& CPU, RegCode Code) : cpu(CPU), code(Code), bitMode(0) {}

Register::Register(Processor& CPU, RegCode Code, unsigned BitModeOverride)
    : cpu(CPU), code(Code), bitMode(BitModeOverride) {
}

bool Register::xmm() {
    return code >= 16; 
}

bool Register::extended() {
    return (code % 16) >= 8; 
}

RegCode Register::index() {
    return (RegCode)(code % 8); 
}

RegPrefix Register::prefix(unsigned short other, bool defLong) {
    return RegPrefix(*this, other, defLong); 
}

RegPrefix Register::prefix(Register& other, bool defLong) {
    return RegPrefix(*this, other.code, defLong); 
}

unsigned char Register::modrm(unsigned short other) {
    return mod_rm(other % 8, REG, code % 8); 
}

unsigned char Register::modrm(Register& other) {
    return mod_rm(other.code % 8, REG, code % 8); 
}

unsigned Register::getBitMode() const {
    if(bitMode)
        return bitMode; 
    else
        return cpu.bitMode; 
}

unsigned Register::getBitMode(const MemAddress& addr) const {
    if(bitMode == 0)
        return addr.bitMode; 
    else
        return bitMode; 
}

MemAddress Register::operator*() const {
    return MemAddress(cpu,code); 
}

ScaledIndex Register::operator*(unsigned char scale) const {
    return ScaledIndex(code, scale); 
}

void Register::swap(MemAddress address) {
    address.other = code; 
    switch(getBitMode(address)) {
    case 8:
        cpu << address.prefix() << '\x86' << address;  break; 
    case 16:
        cpu << address.prefix('\x66') << '\x87' << address;  break; 
    case 32:
    case 64:
        cpu << address.prefix() << '\x87' << address;  break; 
    }
}

void Register::swap(Register& other) {
    if(code == other.code)
        return; 
    if(code == EAX)
        cpu << other.prefix() << byte(0x90+(other.code % 8)); 
    else if(other.code == EAX)
        cpu << prefix() << byte(0x90+(code % 8)); 
    else
        cpu << other.prefix(code) << '\x87' << other.modrm(code); 
}

void Register::copy_address(MemAddress address) {
    address.bitMode = sizeof(void*) * 8; 
    address.other = code; 
    cpu << address.prefix() << '\x8D' << address; 
}

void Register::operator<<=(Register& other) {
    if(other.code != ECX)
        throw "Can only rotate by ECX"; 
    cpu << prefix() << '\xD3' << modrm(EX_4); 
}

void Register::operator>>=(Register& other) {
    if(other.code != ECX)
        throw "Can only rotate by ECX"; 
    cpu << prefix() << '\xD3' << modrm(EX_7); 
}

void Register::rightshift_logical(Register& other) {
    if(other.code != ECX)
        throw "Can only rotate by ECX"; 
    cpu << prefix() << '\xD3' << modrm(EX_5); 
}

void Register::operator+=(unsigned int amount) {
    if(amount == 0) return; 

    if(code == EAX) {
        cpu << prefix() << '\x05' << amount; 
    }
    else if(amount <= CHAR_MAX) {
        cpu << prefix() <<'\x83' << modrm(EX_0) << (byte)amount; 
    }
    else {
        cpu << prefix() << '\x81' << modrm(EX_0) << amount; 
    }
}

void Register::operator+=(MemAddress address) {
    address.other = code; 
    cpu << address.prefix() << '\x03' << address; 
}

void Register::operator+=(Register& other) {
    cpu << other.prefix(*this) << '\x03' << other.modrm(code); 
}

void Register::operator-=(unsigned int amount) {
    if(amount == 0) return; 

    if(code == EAX) {
        cpu << '\x2D' << amount; 
    }
    else if(amount <= CHAR_MAX) {
        cpu << prefix() <<'\x83' << modrm(EX_5) << (byte)amount; 
    }
    else {
        cpu << prefix() << '\x81' << modrm(EX_5) << amount; 
    }
}

void Register::operator-=(Register& other) {
    cpu << other.prefix(*this) << '\x2B' << other.modrm(code); 
}

void Register::operator-=(MemAddress address) {
    address.other = code; 
    cpu << address.prefix() << '\x2B' << address; 
}

void Register::operator*=(MemAddress address) {
    address.other = code; 
    cpu << address.prefix() << '\x0F' << '\xAF' << address; 
}

void Register::multiply_signed(MemAddress address, int value) {
    address.other = code; 
    if(cpu.bitMode == 32 || cpu.bitMode == 64) {
        if(value >= CHAR_MIN && value <= CHAR_MAX)
            cpu << address.prefix() << '\x6B' << address << (char)value; 
        else
            cpu << address.prefix() << '\x69' << address << value; 
    }
    else if(cpu.bitMode == 16) {
        if(value >= CHAR_MIN && value <= CHAR_MAX)
            cpu << address.prefix('\x66') << '\x6B' << address << (char)value; 
        else
            cpu << address.prefix('\x66') << '\x69' << address << (short)value; 
    }
}

void Register::operator-() {
    cpu << prefix(EX_3) << '\xF7' << modrm(EX_3); 
}

void Register::operator--() {
    switch(getBitMode()) {
    case 8:
        cpu << prefix(EX_1) << '\xFE' << modrm(EX_1);  break; 
    case 16:
        cpu << '\x66' << prefix(EX_1) << byte('\x48'+(code % 8));  break; 
    default:
        cpu << prefix(EX_1) << '\xFF' << modrm(EX_1);  break; 
    }
}

void Register::operator++() {
    switch(getBitMode()) {
    case 8:
        cpu << prefix() << '\xFE' << modrm(EX_0);  break; 
    case 16:
        cpu << '\x66' << prefix() << byte('\x40'+(code % 8));  break; 
    default:
        cpu << prefix() << '\xFF' << modrm(EX_0);  break; 
    }
}

void Register::operator~() {
    if(getBitMode() == 8)
        cpu << prefix(EX_2) << '\xF6' << modrm(EX_2); 
    else
        cpu << prefix(EX_2) << '\xF7' << modrm(EX_2); 
}

void Register::operator&=(MemAddress address) {
    address.other = code; 
    cpu << address.prefix() << '\x23' << address; 
}

void Register::operator&=(unsigned long long mask) {
    switch(getBitMode()) {
    case 8:
        cpu << prefix() << '\x80' << modrm(EX_4) << (byte)mask;  break; 
    case 16:
        cpu << prefix('\x66') << '\x81' << modrm(EX_4) << (unsigned short)mask;  break; 
    case 32:
    case 64:
        if(code == EAX)
            cpu << '\x25' << (unsigned)mask; 
        else
            cpu << prefix() << '\x81' << modrm(EX_4) << (unsigned)mask; 
    }
}

void Register::operator&=(Register other) {
    switch(getBitMode()) {
    case 8:
        cpu << prefix(other) << '\x20' << modrm(other.code);  break; 
    case 16:
        cpu << '\x66' << prefix(other) << '\x21' << modrm(other.code);  break; 
    case 32:
    case 64:
        cpu << prefix(other) << '\x21' << modrm(other.code);  break; 
    }
}

void Register::operator^=(MemAddress address) {
    address.other = code; 
    cpu << address.prefix() << '\x33' << address; 
}

void Register::operator^=(Register& other) {
    if(xmm() || other.xmm())
        throw 0; 
    cpu << prefix(other) << '\x31' << modrm(other); 
}

void Register::operator|=(MemAddress address) {
    address.other = code; 
    cpu << address.prefix() << '\x0B' << address; 
}

void Register::operator|=(unsigned long long mask) {
    switch(getBitMode()) {
    case 8:
        cpu << prefix() << '\x80' << modrm(EX_1) << (byte)mask;  break; 
    case 16:
        cpu << '\x66' << prefix() << '\x81' << modrm(EX_1) << (unsigned short)mask;  break; 
    case 32:
    case 64:
        if(code == EAX)
            cpu << '\x0D' << (unsigned)mask; 
        else
            cpu << prefix() << '\x81' << modrm(EX_1) << (unsigned)mask; 
    }
}

void Register::operator=(void* pointer) {
    if(pointer != (void*)0) {
        bitMode = sizeof(void*) * 8; 
        cpu << prefix() << (byte)('\xB8'+(code % 8)) << pointer; 
    }
    else {
        //Special case setting to a null pointer with implied sign extend
        bitMode = 32; 
        *this = (unsigned long long)0; 
    }
}

void Register::operator=(unsigned long long value) {
    switch(getBitMode()) {
    case 8:
        cpu << prefix() << (byte)('\xB0'+(code % 8)) << (byte)value;  break; 
    case 16:
        cpu << '\x66' << prefix() << (byte)('\xB8'+(code % 8)) << (unsigned short)value;  break; 
    case 32:
        cpu << prefix() << (byte)('\xB8'+(code % 8)) << (unsigned int)value;  break; 
    case 64:
        cpu << prefix() << (byte)('\xB8'+(code % 8)) << value;  break; 
    }
}

void* Register::setDeferred(unsigned long long value) {
    void* ptr = 0; 
    switch(getBitMode()) {
    case 8:
        cpu << prefix() << (byte)('\xB0'+(code % 8)); 
        ptr = (void*)cpu.op; 
        cpu << (byte)value;  break; 
    case 16:
        cpu << '\x66' << prefix() << (byte)('\xB8'+(code % 8)); 
        ptr = (void*)cpu.op; 
        cpu << (unsigned short)value;  break; 
    case 32:
        cpu << prefix() << (byte)('\xB8'+(code % 8)); 
        ptr = (void*)cpu.op; 
        cpu << (unsigned int)value;  break; 
    case 64:
        cpu << prefix() << (byte)('\xB8'+(code % 8)); 
        ptr = (void*)cpu.op; 
        cpu << value;  break; 
    }
    return ptr; 
}

void Register::operator=(Register other) {
    if(xmm() != other.xmm())
        throw 0; 
    if(code == other.code)
        return; 

    if(xmm()) {
        if(bitMode == 0 && other.getBitMode() != getBitMode())
            bitMode = other.getBitMode(); 
        switch(getBitMode()) {
            case 32:
                cpu << '\xF3' << prefix(other) << '\x0F' << '\x10' << modrm(other); 
            break; 
            case 64:
                cpu << '\xF2' << prefix(other, true) << '\x0F' << '\x10' << modrm(other); 
            break; 
        }
    }
    else {
        cpu << prefix(other) << '\x89' << modrm(other); 
    }
}

void Register::copy_expanding(MemAddress address) {
    address.other = code; 
    switch(getBitMode(address)) {
    case 8:
        cpu << address.prefix() << '\x0F' << '\xBE' << address;  break; 
    case 16:
        cpu << address.prefix() << '\x0F' << '\xBF' << address;  break; 
    case 32:
        cpu << address.prefix() << '\x63' << address;  break; 
    case 64:
        *this = address;  break; 
    }
}

void Register::copy_zeroing(Register& other) {
    switch(getBitMode()) {
    case 8:
    case 16:
        throw 0; 
    case 32:
    case 64:
        cpu << other.prefix(*this) << '\x0F' << '\xB6' << other.modrm(code);  break; 
    }
}

void Register::operator=(MemAddress addr) {
    addr.other = code; 
    if(xmm()) {
        if(bitMode == 0 && addr.bitMode != getBitMode())
            bitMode = addr.bitMode; 
        switch(addr.bitMode) {
            case 32:
                cpu << addr.prefix('\xF3') << '\x0F' << '\x10' << addr; 
            break; 
            case 64:
                cpu << addr.prefix('\xF2', true) << '\x0F' << '\x10' << addr; 
            break; 
        }
    }
    else {
        switch(getBitMode(addr)) {
        case 8:
            cpu << addr.prefix() << '\x8A' << addr;  break; 
        case 16:
            cpu << addr.prefix('\x66') << '\x8B' << addr;  break; 
        default:
            cpu << addr.prefix() << '\x8B' << addr;  break; 
        }
    }
}

void Register::operator==(Register other) {
    switch(getBitMode()) {
    case 8:
        cpu << other.prefix(*this) << '\x3A' << other.modrm(code);  break; 
    case 16:
        cpu << '\x66' << other.prefix(*this) << '\x3B' << other.modrm(code);  break; 
    case 32:
    case 64:
        cpu << other.prefix(*this) << '\x3B' << other.modrm(code);  break; 
    }
}

void Register::operator==(MemAddress addr) {
    addr.other = code; 
    switch(getBitMode(addr)) {
    case 8:
        cpu << addr.prefix() << '\x3A' << addr;  break; 
    case 16:
        cpu << addr.prefix('\x66') << '\x3B' << addr;  break; 
    case 32:
    case 64:
        cpu << addr.prefix() << '\x3B' << addr;  break; 
    }
}

void Register::operator==(unsigned int test) {
    switch(getBitMode()) {
    case 8:
        cpu << prefix(EX_7) << '\x80' << modrm(EX_7) << (byte)test;  break; 
    case 16:
        cpu << '\x66' << prefix(EX_7) << '\x80' << modrm(EX_7) << (unsigned short)test;  break; 
    case 32:
    case 64:
        cpu << prefix(EX_7) << '\x81' << modrm(EX_7) << test;  break; 
    }
}

unsigned char setConditions[JumpTypeCount-1] = {0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F}; 

void Register::setIf(JumpType condition) {
    if(condition >= Jump)
        throw 0; 
    cpu << prefix() << '\x0F' << setConditions[condition] << modrm(EX_0); 
}

void Register::divide() {
    cpu << prefix() << '\xF7' << modrm(EX_6); 
}

void Register::divide_signed() {
    cpu << prefix() << '\xF7' << modrm(EX_7); 
}

FloatingPointUnit::FloatingPointUnit(Processor& CPU) : cpu(CPU) {}

void FloatingPointUnit::pop() {
    //FSTP ST0
    cpu << '\xDD' << '\xD8'; 
}

void FloatingPointUnit::exchange(FloatReg reg) {
    cpu << '\xD9' << mod_rm(EX_1,REG,reg); 
}

void FloatingPointUnit::init() {
    cpu << '\xDB' << '\xE3'; 
}

void FloatingPointUnit::negate() {
    cpu << '\xD9' << '\xE0'; 
}

void FloatingPointUnit::load_const_0() {
    cpu << '\xD9' << '\xEE'; 
}

void FloatingPointUnit::load_const_1() {
    cpu << '\xD9' << '\xE8'; 
}

void FloatingPointUnit::operator-=(FloatReg reg) {
    cpu << '\xDC' << mod_rm(EX_4,REG,reg); 
}

void FloatingPointUnit::load_double(MemAddress address) {
    address.other = EX_0; 
    cpu << address.prefix() << '\xDD' << address; 
}

void FloatingPointUnit::add_double(MemAddress address) {
    address.other = EX_0; 
    cpu << address.prefix() << '\xDC' << address; 
}

void FloatingPointUnit::mult_double(MemAddress address) {
    address.other = EX_1; 
    cpu << address.prefix() <<'\xDC' << address; 
}

void FloatingPointUnit::div_double(MemAddress address, bool reversed) {
    address.other = reversed ? EX_7 : EX_6; 
    cpu << address.prefix() <<'\xDC' << address; 
}

void FloatingPointUnit::sub_double(MemAddress address, bool reversed) {
    address.other = reversed ? EX_5 : EX_4; 
    cpu << address.prefix() <<'\xDC' << address; 
}

void FloatingPointUnit::add_float(MemAddress address) {
    address.other = EX_0; 
    cpu << address.prefix() <<'\xD8' << address; 
}

void FloatingPointUnit::sub_float(MemAddress address) {
    address.other = EX_4; 
    cpu << address.prefix() <<'\xD8' << address; 
}

void FloatingPointUnit::mult_float(MemAddress address) {
    address.other = EX_1; 
    cpu << address.prefix() <<'\xD8' << address; 
}

void FloatingPointUnit::div_float(MemAddress address) {
    address.other = EX_6; 
    cpu << address.prefix() <<'\xD8' << address; 
}

void FloatingPointUnit::store_double(MemAddress address, bool pop) {
    address.other = pop ? EX_3 : EX_2; 
    cpu << address.prefix() <<'\xDD' << address; 
}

void FloatingPointUnit::load_float(MemAddress address) {
    address.other = EX_0; 
    cpu << address.prefix() <<'\xD9' << address; 
}

void FloatingPointUnit::store_float(MemAddress address, bool pop) {
    address.other = pop ? EX_3 : EX_2; 
    cpu << address.prefix() <<'\xD9' << address; 
}

void FloatingPointUnit::load_dword(MemAddress address) {
    address.other = EX_0; 
    cpu << address.prefix() <<'\xDB' << address; 
}

void FloatingPointUnit::store_dword(MemAddress address, bool pop) {
    address.other = pop ? EX_3 : EX_2; 
    cpu << address.prefix() <<'\xDB' << address; 
}

void FloatingPointUnit::load_qword(MemAddress address) {
    address.other = EX_5; 
    cpu << address.prefix() <<'\xDF' << address; 
}

void FloatingPointUnit::compare_toCPU(FloatReg floatReg, bool pop) {
    if(pop)
        cpu << '\xDF' << mod_rm(EX_5,REG,floatReg); 
    else
        cpu << '\xDB' << mod_rm(EX_5,REG,floatReg); 
}

void FloatingPointUnit::store_control_word(MemAddress address) {
    address.other = EX_7; 
    cpu << address.prefix() <<'\xD9' << address; 
}

void FloatingPointUnit::load_control_word(MemAddress address) {
    address.other = EX_5; 
    cpu << address.prefix() <<'\xD9' << address; 
}

}; 
#endif
