#define _GNU_SOURCE

#include <stdint.h>

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <fenv.h>

#include <string.h>

#include <signal.h>

#include <sys/types.h>

#include <unistd.h>



#include "debug.h"

#include "box64stack.h"

#include "x64emu.h"

#include "x64run.h"

#include "x64emu_private.h"

#include "x64run_private.h"

#include "x64primop.h"

#include "x64trace.h"

#include "x87emu_private.h"

#include "box64context.h"

#include "my_cpuid.h"

#include "bridge.h"

#include "signals.h"

#include "x64shaext.h"

#ifdef DYNAREC

#include "custommem.h"

#include "../dynarec/native_lock.h"

#endif



#include "modrm.h"



#ifdef TEST_INTERPRETER

uintptr_t Test0F(x64test_t *test, rex_t rex, uintptr_t addr, int *step)

#else

uintptr_t Run0F(x64emu_t *emu, rex_t rex, uintptr_t addr, int *step)

#endif

{

    uint8_t opcode;

    uint8_t nextop;

    uint8_t tmp8u;

    int8_t tmp8s;

    int32_t tmp32s, tmp32s2;

    uint32_t tmp32u, tmp32u2;

    uint64_t tmp64u, tmp64u2;

    int64_t tmp64s;

    reg64_t *oped, *opgd;

    sse_regs_t *opex, *opgx, eax1;

    mmx87_regs_t *opem, *opgm, eam1;



#ifdef TEST_INTERPRETER

    x64emu_t *emu = test->emu;

#endif

    opcode = F8;



    switch(opcode) {



        case 0x00:

            if(rex.is32bits) {          /* VERx Ed */

                nextop = F8;

                switch((nextop>>3)&7) {

                    case 0:                 /* SLDT Ew */

                        GETEW(0);

                        EW->word[0] = 0;

                        if(MODREG)

                            EW->word[1] = 0;

                        break;

                    case 1:                 /* STR Ew */

                        GETEW(0);

                        EW->word[0] = 0x7f; // dummy return

                        if(MODREG)

                            EW->word[1] = 0;

                        break;

                    case 4: //VERR

                    case 5: //VERW

                        GETEW(0);

                        if(!EW->word[0])

                            CLEAR_FLAG(F_ZF);

                        else

                            SET_FLAG(F_ZF); // should test if selector is ok

                        break;

                    default:

                        return 0;

                }

            } else

                return 0;

            break;

        case 0x01:                      /* XGETBV, SGDT, etc... */

            nextop = F8;

            GETED(0);

            if(MODREG)

            switch(nextop) {

                case 0xD0:

                    if(R_RCX) {

                        #ifndef TEST_INTERPRETER

                        emit_signal(emu, SIGILL, (void*)R_RIP, 0);

                        #else

                        test->notest = 1;

                        #endif

                    } else {

                        R_RAX = 0b111;   // x87 & SSE & AVX for now

                        R_RDX = 0;

                    }

                    break;

                case 0xE0:

                case 0xE1:

                case 0xE2:

                case 0xE3:

                case 0xE4:

                case 0xE5:

                case 0xE6:

                case 0xE7:  /* SMSW Ew */

                    ED->word[0] = (1<<0) | (1<<4); // only PE and ET set...

                    break;

                case 0xF9:  /* RDTSCP */

                    tmp64u = ReadTSC(emu);

                    if(box64_rdtsc_shift)

                        tmp64u<<=box64_rdtsc_shift;

                    R_RAX = tmp64u & 0xffffffff;

                    R_RDX = tmp64u >> 32;

                    R_RCX = helper_getcpu(emu);

                    #ifdef TEST_INTERPRETER

                    test->notest = 1;

                    #endif

                    break;

                default:

                    return 0;

            } else

                switch((nextop>>3)&7) {

                    case 0:                 /* SGDT Ed */

                        ED->word[0] = 0x7f;    // dummy return...

                        if(rex.is32bits) {

                            *(uint32_t*)(&ED->word[1]) = 0x3000;

                        } else {

                            *(uint64_t*)(&ED->word[1]) = 0xfffffe0000077000LL;

                        }

                        break;

                    case 1:                 /* SIDT Ed */

                        ED->word[0] = 0xfff;    // dummy return, like "disabled"

                        if(rex.is32bits) {

                            *(uint32_t*)(&ED->word[1]) = 0x0000;

                        } else {

                            *(uint64_t*)(&ED->word[1]) = 0xfffffe0000000000LL;

                        }

                        break;

                    case 4:                 /* SMSW Ew */

                        // dummy for now... Do I need to track CR0 state?

                        ED->word[0] = (1<<0) | (1<<4); // only PE and ET set...

                        break;

                    default:

                        return 0;

            }

            break;



        case 0x05:                      /* SYSCALL */

            #ifndef TEST_INTERPRETER

            R_RIP = addr;

            x64Syscall(emu);

            #else

            test->notest = 1;

            #endif

            break;

        case 0x06:                      /* CLTS */

            // this is a privilege opcode...

            #ifndef TEST_INTERPRETER

            emit_signal(emu, SIGSEGV, (void*)R_RIP, 0);

            #endif

            break;



        case 0x08:                      /* INVD */

        case 0x09:                      /* WBINVD */

            // this is a privilege opcode...

            #ifndef TEST_INTERPRETER

            emit_signal(emu, SIGSEGV, (void*)R_RIP, 0);

            #else

            test->notest = 1;

            #endif

            break;



        case 0x0B:                      /* UD2 */

            #ifndef TEST_INTERPRETER

            emit_signal(emu, SIGILL, (void*)R_RIP, 0);

            #else

            test->notest = 1;

            #endif

            break;



        case 0x0D:

            nextop = F8;

            GETED(0);

            switch((nextop>>3)&7) {

                case 1: //PREFETCHW

                    __builtin_prefetch((void*)ED, 1, 0);

                    break;

                default:    //???

                    return 0;

            }

            break;

        case 0x0E:                      /* FEMMS */

            #ifndef TEST_INTERPRETER

            emit_signal(emu, SIGILL, (void*)R_RIP, 0);

            #else

            test->notest = 1;

            #endif

            break;



        case 0x10:                      /* MOVUPS Gx,Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            GX->u128 = EX->u128;

            break;

        case 0x11:                      /* MOVUPS Ex,Gx */

            nextop = F8;

            GETEX(0);

            GETGX;

            EX->u128 = GX->u128;

            break;

        case 0x12:

            nextop = F8;

            GETEX(0);

            GETGX;

            if(MODREG)    /* MOVHLPS Gx,Ex */

                GX->q[0] = EX->q[1];

            else

                GX->q[0] = EX->q[0];    /* MOVLPS Gx,Ex */

            break;

        case 0x13:                      /* MOVLPS Ex,Gx */

            nextop = F8;

            GETEX(0);

            GETGX;

            EX->q[0] = GX->q[0];

            break;

        case 0x14:                      /* UNPCKLPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            GX->ud[3] = EX->ud[1];

            GX->ud[2] = GX->ud[1];

            GX->ud[1] = EX->ud[0];

            break;

        case 0x15:                      /* UNPCKHPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            GX->ud[0] = GX->ud[2];

            GX->ud[1] = EX->ud[2];

            GX->ud[2] = GX->ud[3];

            GX->ud[3] = EX->ud[3];

            break;

        case 0x16:                      /* MOVHPS Gx,Ex */

            nextop = F8;               /* MOVLHPS Gx,Ex (Ex==reg) */

            GETEX(0);

            GETGX;

            GX->q[1] = EX->q[0];

            break;

        case 0x17:                      /* MOVHPS Ex,Gx */

            nextop = F8;

            GETEX(0);

            GETGX;

            EX->q[0] = GX->q[1];

            break;

        case 0x18:                       /* PREFETCHh Ed */

            nextop = F8;

            _GETED(0);

            if(MODREG) {

            } else

            switch((nextop>>3)&7) {

                case 0: //PREFETCHnta

                case 1: //PREFETCH1

                case 2: //PREFETCH2

                case 3: //PREFETCH3

                    __builtin_prefetch((void*)ED, 0, 0); // ignoring wich level of cache

                    break;

                default:    //NOP

                    break;

            }

            break;



        case 0x1F:                      /* NOP (multi-byte) */

            nextop = F8;

            FAKEED(0);

            break;

        case 0x20:                      /* MOV REG, crX */

        case 0x21:                      /* MOV REG, drX */

        case 0x22:                      /* MOV cxR, REG */

        case 0x23:                      /* MOV drX, REG */

            // this is a privilege opcode...

            #ifndef TEST_INTERPRETER

            emit_signal(emu, SIGSEGV, (void*)R_RIP, 0);

            #endif

            break;



        case 0x28:                      /* MOVAPS Gx,Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            GX->q[0] = EX->q[0];

            GX->q[1] = EX->q[1];

            break;

        case 0x29:                      /* MOVAPS Ex,Gx */

            nextop = F8;

            GETEX(0);

            GETGX;

            EX->q[0] = GX->q[0];

            EX->q[1] = GX->q[1];

            break;

        case 0x2A:                      /* CVTPI2PS Gx, Em */

            nextop = F8;

            GETEM(0);

            GETGX;

            GX->f[0] = EM->sd[0];

            GX->f[1] = EM->sd[1];

            break;

        case 0x2B:                      /* MOVNTPS Ex,Gx */

            nextop = F8;

            GETEX(0);

            GETGX;

            EX->q[0] = GX->q[0];

            EX->q[1] = GX->q[1];

            break;

        case 0x2C:                      /* CVTTPS2PI Gm, Ex */

            nextop = F8;

            GETEX(0);

            GETGM;

            tmp64s = EX->f[1];

            if (tmp64s==(int32_t)tmp64s && !isnanf(EX->f[1]))

                GM->sd[1] = (int32_t)tmp64s;

            else

                GM->sd[1] = INT32_MIN;

            tmp64s = EX->f[0];

            if (tmp64s==(int32_t)tmp64s && !isnanf(EX->f[0]))

                GM->sd[0] = (int32_t)tmp64s;

            else

                GM->sd[0] = INT32_MIN;

            break;

        case 0x2D:                      /* CVTPS2PI Gm, Ex */

            // rounding should be done; and indefinite integer should also be assigned if overflow or NaN/Inf

            nextop = F8;

            GETEX(0);

            GETGM;

            for(int i=1; i>=0; --i) {

                if(isnanf(EX->f[i]))

                    tmp64s = INT32_MIN;

                else

                    switch(emu->mxcsr.f.MXCSR_RC) {

                        case ROUND_Nearest: {

                            int round = fegetround();

                            fesetround(FE_TONEAREST);

                            tmp64s = nearbyintf(EX->f[i]);

                            fesetround(round);

                            break;

                        }

                        case ROUND_Down:

                            tmp64s = floorf(EX->f[i]);

                            break;

                        case ROUND_Up:

                            tmp64s = ceilf(EX->f[i]);

                            break;

                        case ROUND_Chop:

                            tmp64s = EX->f[i];

                            break;

                    }

                if (tmp64s==(int32_t)tmp64s)

                    GM->sd[i] = (int32_t)tmp64s;

                else

                    GM->sd[i] = INT32_MIN;

            }

            break;

        case 0x2E:                      /* UCOMISS Gx, Ex */

            // same for now

        case 0x2F:                      /* COMISS Gx, Ex */

            RESET_FLAGS(emu);

            nextop = F8;

            GETEX(0);

            GETGX;

            if(isnan(GX->f[0]) || isnan(EX->f[0])) {

                SET_FLAG(F_ZF); SET_FLAG(F_PF); SET_FLAG(F_CF);

            } else if(isgreater(GX->f[0], EX->f[0])) {

                CLEAR_FLAG(F_ZF); CLEAR_FLAG(F_PF); CLEAR_FLAG(F_CF);

            } else if(isless(GX->f[0], EX->f[0])) {

                CLEAR_FLAG(F_ZF); CLEAR_FLAG(F_PF); SET_FLAG(F_CF);

            } else {

                SET_FLAG(F_ZF); CLEAR_FLAG(F_PF); CLEAR_FLAG(F_CF);

            }

            CLEAR_FLAG(F_OF); CLEAR_FLAG(F_AF); CLEAR_FLAG(F_SF);

            break;



        case 0x31:                   /* RDTSC */

            tmp64u = ReadTSC(emu);

            if(box64_rdtsc_shift)

                tmp64u<<=box64_rdtsc_shift;

            R_RDX = tmp64u>>32;

            R_RAX = tmp64u&0xFFFFFFFF;

            #ifdef TEST_INTERPRETER

            test->notest = 1;

            #endif

            break;



        case 0x38:  // these are some SSE3 opcodes

            opcode = F8;

            switch(opcode) {

                case 0x00:  /* PSHUFB */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    eam1 = *GM;

                    for (int i=0; i<8; ++i) {

                        if(EM->ub[i]&128)

                            GM->ub[i] = 0;

                        else

                            GM->ub[i] = eam1.ub[EM->ub[i]&7];

                    }

                    break;

                case 0x01: /* PHADDW Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i = 0; i < 2; ++i)

                        GM->sw[i] = GM->sw[i * 2 + 0] + GM->sw[i * 2 + 1];

                    if (GM == EM) {

                        GM->sd[1] = GM->sd[0];

                    } else {

                        for (int i = 0; i < 2; ++i)

                            GM->sw[2 + i] = EM->sw[i * 2 + 0] + EM->sw[i * 2 + 1];

                    }

                    break;

                case 0x02: /* PHADDD Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    GM->sd[0] = GM->sd[0] + GM->sd[1];

                    if (GM == EM) {

                        GM->sd[1] = GM->sd[0];

                    } else {

                        GM->sd[1] = EM->sd[0] + EM->sd[1];

                    }

                    break;

                case 0x03: /* PHADDSW Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i = 0; i < 2; ++i) {

                        tmp32s = GM->sw[i * 2 + 0] + GM->sw[i * 2 + 1];

                        GM->sw[i] = (tmp32s < -32768) ? -32768 : ((tmp32s > 32767) ? 32767 : tmp32s);

                    }

                    if (GM == EM) {

                        GM->sd[1] = GM->sd[0];

                    } else {

                        for (int i = 0; i < 2; ++i) {

                            tmp32s = EM->sw[i * 2 + 0] + EM->sw[i * 2 + 1];

                            GM->sw[2 + i] = (tmp32s < -32768) ? -32768 : ((tmp32s > 32767) ? 32767 : tmp32s);

                        }

                    }

                    break;

                case 0x04:  /* PMADDUBSW Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    if(GM==EM) {

                        eam1 = *EM;

                        EM = &eam1;

                    }

                    for (int i=0; i<4; ++i) {

                        tmp32s = (int32_t)(GM->ub[i*2+0])*EM->sb[i*2+0] + (int32_t)(GM->ub[i*2+1])*EM->sb[i*2+1];

                        GM->sw[i] = (tmp32s>32767)?32767:((tmp32s<-32768)?-32768:tmp32s);

                    }

                    break;

                case 0x05: /* PHSUBW Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i = 0; i < 2; ++i)

                        GM->sw[i] = GM->sw[i * 2 + 0] - GM->sw[i * 2 + 1];

                    if (GM == EM) {

                        GM->sd[1] = GM->sd[0];

                    } else {

                        for (int i = 0; i < 2; ++i)

                            GM->sw[2 + i] = EM->sw[i * 2 + 0] - EM->sw[i * 2 + 1];

                    }

                    break;

                case 0x06: /* PHSUBD Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    GM->sd[0] = GM->sd[0] - GM->sd[1];

                    if (GM == EM) {

                        GM->sd[1] = GM->sd[0];

                    } else {

                        GM->sd[1] = EM->sd[0] - EM->sd[1];

                    }

                    break;

                case 0x07: /* PHSUBSW Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i = 0; i < 2; ++i) {

                        tmp32s = GM->sw[i * 2 + 0] - GM->sw[i * 2 + 1];

                        GM->sw[i] = (tmp32s < -32768) ? -32768 : ((tmp32s > 32767) ? 32767 : tmp32s);

                    }

                    if (GM == EM) {

                        GM->sd[1] = GM->sd[0];

                    } else {

                        for (int i = 0; i < 2; ++i) {

                            tmp32s = EM->sw[i * 2 + 0] - EM->sw[i * 2 + 1];

                            GM->sw[2 + i] = (tmp32s < -32768) ? -32768 : ((tmp32s > 32767) ? 32767 : tmp32s);

                        }

                    }

                    break;

                case 0x08: /* PSIGNB Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i = 0; i < 8; ++i)

                        GM->sb[i] *= (EM->sb[i] < 0) ? -1 : ((EM->sb[i] > 0) ? 1 : 0);

                    break;

                case 0x09: /* PSIGNW Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i = 0; i < 4; ++i)

                        GM->sw[i] *= (EM->sw[i] < 0) ? -1 : ((EM->sw[i] > 0) ? 1 : 0);

                    break;

                case 0x0A: /* PSIGND Gm,Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i = 0; i < 2; ++i)

                        GM->sd[i] *= (EM->sd[i] < 0) ? -1 : ((EM->sd[i] > 0) ? 1 : 0);

                    break;

                case 0x0B:  /* PMULHRSW Gm, Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i=0; i<4; ++i) {

                        tmp32s = ((((int32_t)(GM->sw[i])*(int32_t)(EM->sw[i]))>>14) + 1)>>1;

                        GM->uw[i] = tmp32s&0xffff;

                    }

                    break;



                case 0x1C:  /* PABSB Gm, Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i=0; i<8; ++i) {

                        GM->sb[i] = abs(EM->sb[i]);

                    }

                    break;

                case 0x1D:  /* PABSW Gm, Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i=0; i<4; ++i) {

                        GM->sw[i] = abs(EM->sw[i]);

                    }

                    break;

                case 0x1E:  /* PABSD Gm, Em */

                    nextop = F8;

                    GETEM(0);

                    GETGM;

                    for (int i=0; i<2; ++i) {

                        GM->sd[i] = abs(EM->sd[i]);

                    }

                    break;



                case 0xC8:  /* SHA1NEXTE Gx, Ex */

                    nextop = F8;

                    GETGX;

                    GETEX(0);

                    sha1nexte(emu, GX, EX);

                    break;

                case 0xC9:  /* SHA1MSG1 Gx, Ex */

                    nextop = F8;

                    GETGX;

                    GETEX(0);

                    sha1msg1(emu, GX, EX);

                    break;

                case 0xCA:  /* SHA1MSG2 Gx, Ex */

                    nextop = F8;

                    GETGX;

                    GETEX(0);

                    sha1msg2(emu, GX, EX);

                    break;

                case 0xCB:  /* SHA256RNDS2 Gx, Ex (, XMM0) */

                    nextop = F8;

                    GETGX;

                    GETEX(0);

                    sha256rnds2(emu, GX, EX);

                    break;

                case 0xCC:  /* SHA256MSG1 Gx, Ex */

                    nextop = F8;

                    GETGX;

                    GETEX(0);

                    sha256msg1(emu, GX, EX);

                    break;

                case 0xCD:  /* SHA256MSG2 Gx, Ex */

                    nextop = F8;

                    GETGX;

                    GETEX(0);

                    sha256msg2(emu, GX, EX);

                    break;



                case 0xF0: /* MOVBE Gd, Ed*/

                    nextop = F8;

                    GETGD;

                    GETED(0);

                    if(rex.w)

                        GD->q[0] = __builtin_bswap64(ED->q[0]);

                    else

                        GD->q[0] = __builtin_bswap32(ED->dword[0]);

                    break;

                case 0xF1: /* MOVBE Ed, Gd*/

                    nextop = F8;

                    GETGD;

                    GETED(0);

                    if(rex.w)

                        ED->q[0] = __builtin_bswap64(GD->q[0]);

                    else {

                        if(MODREG)

                            ED->q[0] = __builtin_bswap32(GD->dword[0]);

                        else

                            ED->dword[0] = __builtin_bswap32(GD->dword[0]);

                    }

                    break;



                default:

                    return 0;

            }

            break;



        case 0x3A:

            opcode = F8;

            switch(opcode) {

                case 0xF: /* PALIGNR Gm, Em, Ib */

                    nextop = F8;

                    GETEM(1);

                    GETGM;

                    tmp8u = F8;

                    tmp64u = EM->q;

                    if (tmp8u >= 16) {

                        GM->q = 0;

                    } else if (tmp8u >= 8) {

                        tmp8u -= 8;

                        GM->q >>= tmp8u * 8;

                    } else if (tmp8u == 0) {

                        GM->q = tmp64u;

                    } else {

                        GM->q <<= (8 - tmp8u) * 8;

                        GM->q |= (tmp64u >> tmp8u * 8);

                    }

                    break;



                case 0xCC:  /* SHA1RNDS4 Gx, Ex, Ib */

                    nextop = F8;

                    GETGX;

                    GETEX(1);

                    tmp8u = F8;

                    sha1rnds4(emu, GX, EX, tmp8u);

                    break;



                default:

                    return 0;

            }

            break;





        case 0x3F:

            #ifndef TEST_INTERPRETER

            emit_signal(emu, SIGILL, (void*)R_RIP, 0);

            #endif

            break;

        GOCOND(0x40

            , nextop = F8;

            GETED(0);

            GETGD;

            CHECK_FLAGS(emu);

            , if(rex.w) {GD->q[0] = ED->q[0]; } else {GD->q[0] = ED->dword[0];}

            , if(!rex.w) GD->dword[1] = 0;

            ,

        )                               /* 0x40 -> 0x4F CMOVxx Gd,Ed */ // conditional move, no sign



        case 0x50:                      /* MOVMSKPS Gd, Ex */

            nextop = F8;

            GETEX(0);

            GETGD;

            GD->q[0] = 0;

            for(int i=0; i<4; ++i)

                GD->dword[0] |= ((EX->ud[i]>>31)&1)<<i;

            break;

        case 0x51:                      /* SQRTPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->f[i] = sqrtf(EX->f[i]);

            break;

        case 0x52:                      /* RSQRTPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i) {

                if(EX->f[i]==0)

                    GX->f[i] = 1.0f/EX->f[i];

                else if (EX->f[i]<0)

                    GX->f[i] = -NAN;

                else if (isnan(EX->f[i]))

                    GX->f[i] = EX->f[i];

                else if (isinf(EX->f[i]))

                    GX->f[i] = 0.0;

                else

                    GX->f[i] = 1.0f/sqrtf(EX->f[i]);

            }

            #ifdef TEST_INTERPRETER

            test->notest = 1;

            #endif

            break;

        case 0x53:                      /* RCPPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->f[i] = 1.0f/EX->f[i];

            #ifdef TEST_INTERPRETER

            test->notest = 1;

            #endif

            break;

        case 0x54:                      /* ANDPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->ud[i] &= EX->ud[i];

            break;

        case 0x55:                      /* ANDNPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->ud[i] = (~GX->ud[i]) & EX->ud[i];

            break;

        case 0x56:                      /* ORPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->ud[i] |= EX->ud[i];

            break;

        case 0x57:                      /* XORPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->ud[i] ^= EX->ud[i];

            break;

        case 0x58:                      /* ADDPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->f[i] += EX->f[i];

            break;

        case 0x59:                      /* MULPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->f[i] *= EX->f[i];

            break;

        case 0x5A:                      /* CVTPS2PD Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            GX->d[1] = EX->f[1];

            GX->d[0] = EX->f[0];

            break;

        case 0x5B:                      /* CVTDQ2PS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            GX->f[0] = EX->sd[0];

            GX->f[1] = EX->sd[1];

            GX->f[2] = EX->sd[2];

            GX->f[3] = EX->sd[3];

            break;

        case 0x5C:                      /* SUBPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->f[i] -= EX->f[i];

            break;

        case 0x5D:                      /* MINPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i) {

                if (isnan(GX->f[i]) || isnan(EX->f[i]) || isless(EX->f[i], GX->f[i]))

                    GX->f[i] = EX->f[i];

            }

            break;

        case 0x5E:                      /* DIVPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i)

                GX->f[i] /= EX->f[i];

            break;

        case 0x5F:                      /* MAXPS Gx, Ex */

            nextop = F8;

            GETEX(0);

            GETGX;

            for(int i=0; i<4; ++i) {

                if (isnan(GX->f[i]) || isnan(EX->f[i]) || isgreater(EX->f[i], GX->f[i]))

                    GX->f[i] = EX->f[i];

            }

            break;

        case 0x60:                      /* PUNPCKLBW Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->ub[7] = EM->ub[3];

            GM->ub[6] = GM->ub[3];

            GM->ub[5] = EM->ub[2];

            GM->ub[4] = GM->ub[2];

            GM->ub[3] = EM->ub[1];

            GM->ub[2] = GM->ub[1];

            GM->ub[1] = EM->ub[0];

            break;

        case 0x61:                      /* PUNPCKLWD Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->uw[3] = EM->uw[1];

            GM->uw[2] = GM->uw[1];

            GM->uw[1] = EM->uw[0];

            break;

        case 0x62:                      /* PUNPCKLDQ Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->ud[1] = EM->ud[0];

            break;

        case 0x63:                      /* PACKSSWB Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->sb[0] = (GM->sw[0] > 127) ? 127 : ((GM->sw[0] < -128) ? -128 : GM->sw[0]);

            GM->sb[1] = (GM->sw[1] > 127) ? 127 : ((GM->sw[1] < -128) ? -128 : GM->sw[1]);

            GM->sb[2] = (GM->sw[2] > 127) ? 127 : ((GM->sw[2] < -128) ? -128 : GM->sw[2]);

            GM->sb[3] = (GM->sw[3] > 127) ? 127 : ((GM->sw[3] < -128) ? -128 : GM->sw[3]);

            if(EM==GM)

                GM->ud[1] = GM->ud[0];

            else {

                GM->sb[4] = (EM->sw[0] > 127) ? 127 : ((EM->sw[0] < -128) ? -128 : EM->sw[0]);

                GM->sb[5] = (EM->sw[1] > 127) ? 127 : ((EM->sw[1] < -128) ? -128 : EM->sw[1]);

                GM->sb[6] = (EM->sw[2] > 127) ? 127 : ((EM->sw[2] < -128) ? -128 : EM->sw[2]);

                GM->sb[7] = (EM->sw[3] > 127) ? 127 : ((EM->sw[3] < -128) ? -128 : EM->sw[3]);

            }

            break;

        case 0x64:                       /* PCMPGTB Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i = 0; i < 8; i++) {

                GM->ub[i] = (GM->sb[i] > EM->sb[i]) ? 0xFF : 0;

            }

            break;

        case 0x65:                       /* PCMPGTW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i = 0; i < 4; i++) {

                GM->uw[i] = (GM->sw[i] > EM->sw[i]) ? 0xFFFF : 0;

            }

            break;

        case 0x66:                       /* PCMPGTD Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i = 0; i < 2; i++) {

                GM->ud[i] = (GM->sd[i] > EM->sd[i]) ? 0xFFFFFFFF : 0;

            }

            break;

        case 0x67:                       /* PACKUSWB Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i)

                GM->ub[i] = (GM->sw[i]<0)?0:((GM->sw[i]>0xff)?0xff:GM->sw[i]);

            if(EM==GM)

                GM->ud[1] = GM->ud[0];

            else

                for(int i=0; i<4; ++i)

                    GM->ub[4+i] = (EM->sw[i]<0)?0:((EM->sw[i]>0xff)?0xff:EM->sw[i]);

            break;

        case 0x68:                       /* PUNPCKHBW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i)

                GM->ub[2 * i] = GM->ub[i + 4];

            if(EM==GM)

                for(int i=0; i<4; ++i)

                    GM->ub[2 * i + 1] = GM->ub[2 * i];

            else

                for(int i=0; i<4; ++i)

                    GM->ub[2 * i + 1] = EM->ub[i + 4];

            break;

        case 0x69:                       /* PUNPCKHWD Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<2; ++i)

                GM->uw[2 * i] = GM->uw[i + 2];

            if(EM==GM)

                for(int i=0; i<2; ++i)

                    GM->uw[2 * i + 1] = GM->uw[2 * i];

            else

                for(int i=0; i<2; ++i)

                    GM->uw[2 * i + 1] = EM->uw[i + 2];

            break;

        case 0x6A:                       /* PUNPCKHDQ Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->ud[0] = GM->ud[1];

            if(EM!=GM)

                GM->ud[1] = EM->ud[1];

            break;

        case 0x6B:                       /* PACKSSDW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<2; ++i)

                GM->sw[i] = (GM->sd[i]<-32768)?-32768:((GM->sd[i]>32767)?32767:GM->sd[i]);

            if(EM==GM)

                GM->ud[1] = GM->ud[0];

            else

                for(int i=0; i<2; ++i)

                    GM->sw[2+i] = (EM->sd[i]<-32768)?-32768:((EM->sd[i]>32767)?32767:EM->sd[i]);

            break;



        case 0x6E:                      /* MOVD Gm, Ed */

            nextop = F8;

            GETED(0);

            GETGM;

            if(rex.w)

                GM->q = ED->q[0];

            else

                GM->q = ED->dword[0];    // zero extended

            break;

        case 0x6F:                      /* MOVQ Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->q = EM->q;

            break;

        case 0x70:                       /* PSHUFW Gm, Em, Ib */

            nextop = F8;

            GETEM(1);

            GETGM;

            tmp8u = F8;

            if(EM==GM) {eam1 = *GM; EM = &eam1;}   // copy is needed

            for(int i=0; i<4; ++i)

                GM->uw[i] = EM->uw[(tmp8u>>(i*2))&3];

            break;

        case 0x71:  /* GRP */

            nextop = F8;

            GETEM(1);

            tmp8u = F8;

            switch((nextop>>3)&7) {

                case 2:                 /* PSRLW Em, Ib */

                    if(tmp8u>15)

                        {EM->q = 0;}

                    else

                        for (int i=0; i<4; ++i) EM->uw[i] >>= tmp8u;

                    break;

                case 4:                 /* PSRAW Em, Ib */

                    if(tmp8u>15) tmp8u = 15;

                    for (int i=0; i<4; ++i) EM->sw[i] >>= tmp8u;

                    break;

                case 6:                 /* PSLLW Em, Ib */

                    if(tmp8u>15)

                        {EM->q = 0;}

                    else

                        for (int i=0; i<4; ++i) EM->uw[i] <<= tmp8u;

                    break;

                default:

                    return 0;

            }

            break;

        case 0x72:  /* GRP */

            nextop = F8;

            GETEM(1);

            switch((nextop>>3)&7) {

                case 2:                 /* PSRLD Em, Ib */

                    tmp8u = F8;

                    if(tmp8u>31)

                        {EM->q = 0;}

                    else

                        for (int i=0; i<2; ++i) EM->ud[i] >>= tmp8u;

                    break;

                case 4:                 /* PSRAD Em, Ib */

                    tmp8u = F8;

                    if (tmp8u > 31) tmp8u = 31;

                    for (int i=0; i<2; ++i) EM->sd[i] >>= tmp8u;

                    break;

                case 6:                 /* PSLLD Em, Ib */

                    tmp8u = F8;

                    if(tmp8u>31)

                        {EM->q = 0;}

                    else

                        for (int i=0; i<2; ++i) EM->ud[i] <<= tmp8u;

                    break;

                default:

                    return 0;

            }

            break;

        case 0x73:  /* GRP */

            nextop = F8;

            GETEM(1);

            switch((nextop>>3)&7) {

                case 2:                 /* PSRLQ Em, Ib */

                    tmp8u = F8;

                    if(tmp8u>63)

                        {EM->q = 0;}

                    else

                        {EM->q >>= tmp8u;}

                    break;

                case 6:                 /* PSLLQ Em, Ib */

                    tmp8u = F8;

                    if(tmp8u>63)

                        {EM->q = 0;}

                    else

                        {EM->q <<= tmp8u;}

                    break;

                default:

                    return 0;

            }

            break;

        case 0x74:                       /* PCMPEQB Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i = 0; i < 8; i++) {

                GM->ub[i] = (GM->sb[i] == EM->sb[i]) ? 0xFF : 0;

            }

            break;

        case 0x75:                       /* PCMPEQW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i = 0; i < 4; i++) {

                GM->uw[i] = (GM->sw[i] == EM->sw[i]) ? 0xFFFF : 0;

            }

            break;

        case 0x76:                       /* PCMPEQD Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i = 0; i < 2; i++) {

                GM->ud[i] = (GM->sd[i] == EM->sd[i]) ? 0xFFFFFFFF : 0;

            }

            break;

        case 0x77:                      /* EMMS */

            // empty MMX, FPU now usable

            emu->top = 0;

            emu->fpu_stack = 0;

            break;



        case 0x7E:                       /* MOVD Ed, Gm */

            nextop = F8;

            GETED(0);

            GETGM;

            if(rex.w)

                ED->q[0] = GM->q;

            else {

                if(MODREG)

                    ED->q[0] = GM->ud[0];

                else

                    ED->dword[0] = GM->ud[0];

            }

            break;

        case 0x7F:                      /* MOVQ Em, Gm */

            nextop = F8;

            GETEM(0);

            GETGM;

            EM->q = GM->q;

            break;

        GOCOND(0x80

            , tmp32s = F32S; CHECK_FLAGS(emu);

            #ifdef TEST_INTERPRETER

            test->notest = 1;

            #endif

            , addr += tmp32s;

            ,,

        )                               /* 0x80 -> 0x8F Jxx */ //STEP3

        GOCOND(0x90

            , nextop = F8; CHECK_FLAGS(emu);

            GETEB(0);

            , EB->byte[0]=1;

            , EB->byte[0]=0;

            ,

        )                               /* 0x90 -> 0x9F SETxx Eb */

        case 0xA0:                      /* PUSH FS */

            if(rex.is32bits)

                Push32(emu, emu->segs[_FS]);

            else

                Push64(emu, emu->segs[_FS]);

            break;

        case 0xA1:                      /* POP FS */

            if(rex.is32bits)

                emu->segs[_FS] = Pop32(emu);

            else

                emu->segs[_FS] = Pop64(emu);

            emu->segs_serial[_FS] = 0;

            break;

        case 0xA2:                      /* CPUID */

            tmp32u = R_EAX;

            my_cpuid(emu, tmp32u);

            #ifdef TEST_INTERPRETER

            test->notest = 1;

            #endif

            break;

        case 0xA3:                      /* BT Ed,Gd */

            CHECK_FLAGS(emu);

            nextop = F8;

            GETED(0);

            GETGD;

            tmp32s = GD->sdword[0];

            tmp8u=tmp32s&(rex.w?63:31);

            tmp32s >>= (rex.w?6:5);

            if(!MODREG)

            {

                #ifdef TEST_INTERPRETER

                test->memaddr=((test->memaddr)+(tmp32s<<(rex.w?3:2)));

                if(rex.w)

                    *(uint64_t*)test->mem = *(uint64_t*)test->memaddr;

                else

                    *(uint32_t*)test->mem = *(uint32_t*)test->memaddr;

                #else

                ED=(reg64_t*)(((uintptr_t)(ED))+(tmp32s<<(rex.w?3:2)));

                #endif

            }

            if(rex.w) {

                if(ED->q[0] & (1LL<<tmp8u))

                    SET_FLAG(F_CF);

                else

                    CLEAR_FLAG(F_CF);

            } else {

                if(ED->dword[0] & (1<<tmp8u))

                    SET_FLAG(F_CF);

                else

                    CLEAR_FLAG(F_CF);

            }

            break;

        case 0xA4:                      /* SHLD Ed,Gd,Ib */

            nextop = F8;

            GETED(1);

            GETGD;

            tmp8u = F8;

            if(rex.w)

                ED->q[0] = shld64(emu, ED->q[0], GD->q[0], tmp8u);

            else {

                if(MODREG)

                    ED->q[0] = shld32(emu, ED->dword[0], GD->dword[0], tmp8u);

                else

                    ED->dword[0] = shld32(emu, ED->dword[0], GD->dword[0], tmp8u);

            }

            break;

        case 0xA5:                      /* SHLD Ed,Gd,CL */

            nextop = F8;

            GETED(0);

            GETGD;

            tmp8u = R_CL;

            if(rex.w)

                ED->q[0] = shld64(emu, ED->q[0], GD->q[0], tmp8u);

            else {

                if(MODREG)

                    ED->q[0] = shld32(emu, ED->dword[0], GD->dword[0], tmp8u);

                else

                    ED->dword[0] = shld32(emu, ED->dword[0], GD->dword[0], tmp8u);

            }

            break;



        case 0xA8:                      /* PUSH GS */

            if(rex.is32bits)

                Push32(emu, emu->segs[_GS]);

            else

                Push64(emu, emu->segs[_GS]);

            break;

        case 0xA9:                      /* POP GS */

            if(rex.is32bits)

                emu->segs[_GS] = Pop32(emu);

            else

                emu->segs[_GS] = Pop64(emu);

            emu->segs_serial[_FS] = 0;

            break;



        case 0xAB:                      /* BTS Ed,Gd */

            CHECK_FLAGS(emu);

            nextop = F8;

            GETED(0);

            GETGD;

            tmp64s = rex.w?GD->sq[0]:GD->sdword[0];

            tmp8u=tmp64s&(rex.w?63:31);

            tmp64s >>= (rex.w?6:5);

            if(!MODREG)

            {

                #ifdef TEST_INTERPRETER

                test->memaddr=((test->memaddr)+(tmp64s<<(rex.w?3:2)));

                if(rex.w)

                    *(uint64_t*)test->mem = *(uint64_t*)test->memaddr;

                else

                    *(uint32_t*)test->mem = *(uint32_t*)test->memaddr;

                #else

                ED=(reg64_t*)(((uintptr_t)(ED))+(tmp64s<<(rex.w?3:2)));

                #endif

            }

            if(rex.w) {

                if(ED->q[0] & (1LL<<tmp8u))

                    SET_FLAG(F_CF);

                else {

                    ED->q[0] |= (1LL<<tmp8u);

                    CLEAR_FLAG(F_CF);

                }

            } else {

                if(ED->dword[0] & (1<<tmp8u))

                    SET_FLAG(F_CF);

                else {

                    ED->dword[0] |= (1<<tmp8u);

                    CLEAR_FLAG(F_CF);

                }

                if(MODREG)

                    ED->dword[1] = 0;

            }

            break;

        case 0xAC:                      /* SHRD Ed,Gd,Ib */

        case 0xAD:                      /* SHRD Ed,Gd,CL */

            nextop = F8;

            GETED((nextop==0xAC)?1:0);

            GETGD;

            tmp8u = (opcode==0xAC)?(F8):R_CL;

            if(rex.w)

                ED->q[0] = shrd64(emu, ED->q[0], GD->q[0], tmp8u);

            else {

                if(MODREG)

                    ED->q[0] = shrd32(emu, ED->dword[0], GD->dword[0], tmp8u);

                else

                    ED->dword[0] = shrd32(emu, ED->dword[0], GD->dword[0], tmp8u);

            }

            break;

        case 0xAE:                      /* Grp Ed (SSE) */

            nextop = F8;

            if(MODREG)

                switch(nextop) {

                    case 0xE8:

                        return addr;            /* LFENCE */

                    case 0xF0:

                        return addr;            /* MFENCE */

                    case 0xF8:

                        return addr;            /* SFENCE */

                    default:

                        return 0;

                }

            else

            switch((nextop>>3)&7) {

                case 0:                 /* FXSAVE Ed */

                    _GETED(0);

                    #ifdef TEST_INTERPRETER

                    emu->sw.f.F87_TOP = emu->top&7;

                    #else

                    if(rex.w)

                        fpu_fxsave64(emu, ED);

                    else

                        fpu_fxsave32(emu, ED);

                    #endif

                    break;

                case 1:                 /* FXRSTOR Ed */

                    _GETED(0);

                    if(rex.w)

                        fpu_fxrstor64(emu, ED);

                    else

                        fpu_fxrstor32(emu, ED);

                    break;

                case 2:                 /* LDMXCSR Md */

                    GETED(0);

                    emu->mxcsr.x32 = ED->dword[0];

                    #ifndef TEST_INTERPRETER

                    if(box64_sse_flushto0)

                        applyFlushTo0(emu);

                    #endif

                    break;

                case 3:                 /* STMXCSR Md */

                    GETED(0);

                    ED->dword[0] = emu->mxcsr.x32;

                    break;

                case 4:                 /* XSAVE Ed */

                    _GETED(0);

                    #ifdef TEST_INTERPRETER

                    emu->sw.f.F87_TOP = emu->top&7;

                    #else

                    fpu_xsave(emu, ED, rex.is32bits);

                    #endif

                    break;

                case 5:                 /* XRSTOR Ed */

                    _GETED(0);

                    fpu_xrstor(emu, ED, rex.is32bits);

                    break;

                case 7:                 /* CLFLUSH Ed */

                    _GETED(0);

                    #if defined(DYNAREC) && !defined(TEST_INTERPRETER)

                    if(box64_dynarec)

                        cleanDBFromAddressRange((uintptr_t)ED, 8, 0);

                    #endif

                    break;

                default:

                    return 0;

            }

            break;

        case 0xAF:                      /* IMUL Gd,Ed */

            nextop = F8;

            GETED(0);

            GETGD;

            if(rex.w)

                GD->q[0] = imul64(emu, GD->q[0], ED->q[0]);

            else

                GD->q[0] = imul32(emu, GD->dword[0], ED->dword[0]);

            break;

        case 0xB0:                      /* CMPXCHG Eb,Gb */

            nextop = F8;

            GETEB(0);

            GETGB;

            cmp8(emu, R_AL, EB->byte[0]);

            if(ACCESS_FLAG(F_ZF)) {

                EB->byte[0] = GB;

            } else {

                R_AL = EB->byte[0];

            }

            break;

        case 0xB1:                      /* CMPXCHG Ed,Gd */

            nextop = F8;

            GETED(0);

            GETGD;

            if(rex.w) {

                cmp64(emu, R_RAX, ED->q[0]);

                if(ACCESS_FLAG(F_ZF)) {

                    ED->q[0] = GD->q[0];

                } else {

                    R_RAX = ED->q[0];

                }

            } else {

                cmp32(emu, R_EAX, ED->dword[0]);

                if(ACCESS_FLAG(F_ZF)) {

                    if(MODREG)

                        ED->q[0] = GD->dword[0];

                    else

                        ED->dword[0] = GD->dword[0];

                    R_RAX = R_EAX;   // to erase upper part of RAX

                } else {

                    R_RAX = ED->dword[0];

                }

            }

            break;

        case 0xB3:                      /* BTR Ed,Gd */

            CHECK_FLAGS(emu);

            nextop = F8;

            GETED(0);

            GETGD;

            tmp64s = rex.w?GD->sq[0]:GD->sdword[0];

            tmp8u=tmp64s&(rex.w?63:31);

            tmp64s >>= (rex.w?6:5);

            if(!MODREG)

            {

                #ifdef TEST_INTERPRETER

                test->memaddr=((test->memaddr)+(tmp64s<<(rex.w?3:2)));

                if(rex.w)

                    *(uint64_t*)test->mem = *(uint64_t*)test->memaddr;

                else

                    *(uint32_t*)test->mem = *(uint32_t*)test->memaddr;

                #else

                ED=(reg64_t*)(((uintptr_t)(ED))+(tmp64s<<(rex.w?3:2)));

                #endif

            }

            if(rex.w) {

                if(ED->q[0] & (1LL<<tmp8u)) {

                    SET_FLAG(F_CF);

                    ED->q[0] ^= (1LL<<tmp8u);

                } else

                    CLEAR_FLAG(F_CF);

            } else {

                if(ED->dword[0] & (1<<tmp8u)) {

                    SET_FLAG(F_CF);

                    ED->dword[0] ^= (1<<tmp8u);

                } else

                    CLEAR_FLAG(F_CF);

                if(MODREG)

                    ED->dword[1] = 0;

            }

            break;



        case 0xB6:                      /* MOVZX Gd,Eb */

            nextop = F8;

            GETEB(0);

            GETGD;

            GD->q[0] = EB->byte[0];

            break;

        case 0xB7:                      /* MOVZX Gd,Ew */

            nextop = F8;

            GETEW(0);

            GETGD;

            GD->q[0] = EW->word[0];

            break;



        case 0xBA:

            nextop = F8;

            switch((nextop>>3)&7) {

                case 4:                 /* BT Ed,Ib */

                    CHECK_FLAGS(emu);

                    GETED(1);

                    tmp8u = F8;

                    if(rex.w) {

                        tmp8u&=63;

                        if(ED->q[0] & (1LL<<tmp8u))

                            SET_FLAG(F_CF);

                        else

                            CLEAR_FLAG(F_CF);

                    } else {

                        tmp8u&=31;

                        if(ED->dword[0] & (1<<tmp8u))

                            SET_FLAG(F_CF);

                        else

                            CLEAR_FLAG(F_CF);

                    }

                    break;

                case 5:             /* BTS Ed, Ib */

                    CHECK_FLAGS(emu);

                    GETED(1);

                    tmp8u = F8;

                    if(rex.w) {

                        tmp8u&=63;

                        if(ED->q[0] & (1LL<<tmp8u)) {

                            SET_FLAG(F_CF);

                        } else {

                            ED->q[0] ^= (1LL<<tmp8u);

                            CLEAR_FLAG(F_CF);

                        }

                    } else {

                        tmp8u&=31;

                        if(ED->dword[0] & (1<<tmp8u)) {

                            SET_FLAG(F_CF);

                        } else {

                            ED->dword[0] ^= (1<<tmp8u);

                            CLEAR_FLAG(F_CF);

                        }

                        if(MODREG)

                            ED->dword[1] = 0;

                    }

                    break;

                case 6:             /* BTR Ed, Ib */

                    CHECK_FLAGS(emu);

                    GETED(1);

                    tmp8u = F8;

                    if(rex.w) {

                        tmp8u&=63;

                        if(ED->q[0] & (1LL<<tmp8u)) {

                            SET_FLAG(F_CF);

                            ED->q[0] ^= (1LL<<tmp8u);

                        } else

                            CLEAR_FLAG(F_CF);

                    } else {

                        tmp8u&=31;

                        if(ED->dword[0] & (1<<tmp8u)) {

                            SET_FLAG(F_CF);

                            ED->dword[0] ^= (1<<tmp8u);

                        } else

                            CLEAR_FLAG(F_CF);

                        if(MODREG)

                            ED->dword[1] = 0;

                    }

                    break;

                case 7:             /* BTC Ed, Ib */

                    CHECK_FLAGS(emu);

                    GETED(1);

                    tmp8u = F8;

                    if(rex.w) {

                        tmp8u&=63;

                        if(ED->q[0] & (1LL<<tmp8u))

                            SET_FLAG(F_CF);

                        else

                            CLEAR_FLAG(F_CF);

                        ED->q[0] ^= (1LL<<tmp8u);

                    } else {

                        tmp8u&=31;

                        if(ED->dword[0] & (1<<tmp8u))

                            SET_FLAG(F_CF);

                        else

                            CLEAR_FLAG(F_CF);

                        ED->dword[0] ^= (1<<tmp8u);

                        if(MODREG)

                            ED->dword[1] = 0;

                    }

                    break;



                default:

                    return 0;

            }

            break;

        case 0xBB:                      /* BTC Ed,Gd */

            CHECK_FLAGS(emu);

            nextop = F8;

            GETED(0);

            GETGD;

            tmp64s = rex.w?GD->sq[0]:GD->sdword[0];

            tmp8u=tmp64s&(rex.w?63:31);

            tmp64s >>= (rex.w?6:5);

            if(!MODREG)

            {

                #ifdef TEST_INTERPRETER

                test->memaddr=((test->memaddr)+(tmp64s<<(rex.w?3:2)));

                if(rex.w)

                    *(uint64_t*)test->mem = *(uint64_t*)test->memaddr;

                else

                    *(uint32_t*)test->mem = *(uint32_t*)test->memaddr;

                #else

                ED=(reg64_t*)(((uintptr_t)(ED))+(tmp64s<<(rex.w?3:2)));

                #endif

            }

            if(rex.w) {

                if(ED->q[0] & (1LL<<tmp8u))

                    SET_FLAG(F_CF);

                else

                    CLEAR_FLAG(F_CF);

                ED->q[0] ^= (1LL<<tmp8u);

            } else {

                if(ED->dword[0] & (1<<tmp8u))

                    SET_FLAG(F_CF);

                else

                    CLEAR_FLAG(F_CF);

                ED->dword[0] ^= (1<<tmp8u);

                if(MODREG)

                    ED->dword[1] = 0;

            }

            break;

        case 0xBC:                      /* BSF Ed,Gd */

            RESET_FLAGS(emu);

            nextop = F8;

            GETED(0);

            GETGD;

            if(rex.w) {

                tmp64u = ED->q[0];

                if(tmp64u) {

                    CLEAR_FLAG(F_ZF);

                    tmp8u = 0;

                    while(!(tmp64u&(1LL<<tmp8u))) ++tmp8u;

                    GD->q[0] = tmp8u;

                } else {

                    SET_FLAG(F_ZF);

                }

            } else {

                tmp32u = ED->dword[0];

                if(tmp32u) {

                    CLEAR_FLAG(F_ZF);

                    tmp8u = 0;

                    while(!(tmp32u&(1<<tmp8u))) ++tmp8u;

                    GD->q[0] = tmp8u;

                } else {

                    SET_FLAG(F_ZF);

                }

            }

            break;

        case 0xBD:                      /* BSR Ed,Gd */

            RESET_FLAGS(emu);

            nextop = F8;

            GETED(0);

            GETGD;

            if(rex.w) {

                tmp64u = ED->q[0];

                if(tmp64u) {

                    CLEAR_FLAG(F_ZF);

                    tmp8u = 63;

                    while(!(tmp64u&(1LL<<tmp8u))) --tmp8u;

                    GD->q[0] = tmp8u;

                } else {

                    SET_FLAG(F_ZF);

                }

            } else {

                tmp32u = ED->dword[0];

                if(tmp32u) {

                    CLEAR_FLAG(F_ZF);

                    tmp8u = 31;

                    while(!(tmp32u&(1<<tmp8u))) --tmp8u;

                    GD->q[0] = tmp8u;

                } else {

                    SET_FLAG(F_ZF);

                }

            }

            break;

        case 0xBE:                      /* MOVSX Gd,Eb */

            nextop = F8;

            GETEB(0);

            GETGD;

            if(rex.w)

                GD->sq[0] = EB->sbyte[0];

            else {

                GD->sdword[0] = EB->sbyte[0];

                GD->dword[1] = 0;

            }

            break;

        case 0xBF:                      /* MOVSX Gd,Ew */

            nextop = F8;

            GETEW(0);

            GETGD;

            if(rex.w)

                GD->sq[0] = EW->sword[0];

            else {

                GD->sdword[0] = EW->sword[0];

                GD->dword[1] = 0;

            }

            break;

        case 0xC0:                      /* XADD Gb,Eb */

            nextop = F8;

            GETEB(0);

            GETGB;

            tmp8u = add8(emu, EB->byte[0], GB);

            GB = EB->byte[0];

            EB->byte[0] = tmp8u;

            break;

        case 0xC1:                      /* XADD Gd,Ed */

            nextop = F8;

            GETED(0);

            GETGD;

            if(rex.w) {

                tmp64u = add64(emu, ED->q[0], GD->q[0]);

                GD->q[0] = ED->q[0];

                ED->q[0] = tmp64u;

            } else {

                tmp32u = add32(emu, ED->dword[0], GD->dword[0]);

                GD->q[0] = ED->dword[0];

                if(MODREG)

                    ED->q[0] = tmp32u;

                else

                    ED->dword[0] = tmp32u;

            }

            break;

        case 0xC2:                      /* CMPPS Gx, Ex, Ib */

            nextop = F8;

            GETEX(1);

            GETGX;

            tmp8u = F8;

            for(int i=0; i<4; ++i) {

                tmp8s = 0;

                switch(tmp8u&7) {

                    case 0: tmp8s=(GX->f[i] == EX->f[i]); break;

                    case 1: tmp8s=isless(GX->f[i], EX->f[i]); break;

                    case 2: tmp8s=islessequal(GX->f[i], EX->f[i]); break;

                    case 3: tmp8s=isnan(GX->f[i]) || isnan(EX->f[i]); break;

                    case 4: tmp8s=(GX->f[i] != EX->f[i]); break;

                    case 5: tmp8s=isnan(GX->f[i]) || isnan(EX->f[i]) || isgreaterequal(GX->f[i], EX->f[i]); break;

                    case 6: tmp8s=isnan(GX->f[i]) || isnan(EX->f[i]) || isgreater(GX->f[i], EX->f[i]); break;

                    case 7: tmp8s=!isnan(GX->f[i]) && !isnan(EX->f[i]); break;

                }

                GX->ud[i]=(tmp8s)?0xffffffff:0;

            }

            break;

        case 0xC3:                      /* MOVNTI Ed, Gd */

            nextop = F8;

            GETED(0);

            GETGD;

            if(rex.w)

                ED->q[0] = GD->q[0];

            else {

                if(MODREG)

                    ED->q[0] = GD->dword[0];

                else

                    ED->dword[0] = GD->dword[0];

            }

            break;

        case 0xC4:                      /* PINSRW Gm,Ew,Ib */

            nextop = F8;

            GETED(1);

            GETGM;

            tmp8u = F8;

            GM->uw[tmp8u&3] = ED->word[0];   // only low 16bits

            break;

        case 0xC5:                       /* PEXTRW Gw,Em,Ib */

            nextop = F8;

            GETEM(1);

            GETGD;

            tmp8u = F8;

            GD->q[0] = EM->uw[tmp8u&3];  // 16bits extract, 0 extended

            break;

        case 0xC6:                      /* SHUFPS Gx, Ex, Ib */

            nextop = F8;

            GETEX(1);

            GETGX;

            tmp8u = F8;

            for(int i=0; i<2; ++i) {

                eax1.ud[i] = GX->ud[(tmp8u>>(i*2))&3];

            }

            for(int i=2; i<4; ++i) {

                eax1.ud[i] = EX->ud[(tmp8u>>(i*2))&3];

            }

            GX->q[0] = eax1.q[0];

            GX->q[1] = eax1.q[1];

            break;

        case 0xC7:

            CHECK_FLAGS(emu);

            nextop = F8;

            GETE8xw(0);

            switch((nextop>>3)&7) {

                case 1:     /* CMPXCHG8B Eq */

                    if(rex.w) {

                        tmp64u = ED->q[0];

                        tmp64u2= ED->q[1];

                        if(R_RAX == tmp64u && R_RDX == tmp64u2) {

                            SET_FLAG(F_ZF);

                            ED->q[0] = R_EBX;

                            ED->q[1] = R_ECX;

                        } else {

                            CLEAR_FLAG(F_ZF);

                            R_RAX = tmp64u;

                            R_RDX = tmp64u2;

                        }

                    } else {

                        tmp32u = ED->dword[0];

                        tmp32u2= ED->dword[1];

                        if(R_EAX == tmp32u && R_EDX == tmp32u2) {

                            SET_FLAG(F_ZF);

                            ED->dword[0] = R_EBX;

                            ED->dword[1] = R_ECX;

                        } else {

                            CLEAR_FLAG(F_ZF);

                            R_RAX = tmp32u;

                            R_RDX = tmp32u2;

                        }

                    }

                    break;

                case 6:     /* RDRAND Ed */

                    RESET_FLAGS(emu);

                    CLEAR_FLAG(F_OF);

                    CLEAR_FLAG(F_SF);

                    CLEAR_FLAG(F_PF);

                    CLEAR_FLAG(F_ZF);

                    CLEAR_FLAG(F_AF);

                    SET_FLAG(F_CF);

                    if(rex.w)

                        ED->q[0] = get_random64();

                    else {

                        ED->dword[0] = get_random32();

                        if(MODREG)

                            ED->dword[1] = 1;

                    }

                    break;

                default:

                    return 0;

            }

            break;

        case 0xC8:

        case 0xC9:

        case 0xCA:

        case 0xCB:

        case 0xCC:

        case 0xCD:

        case 0xCE:

        case 0xCF:                  /* BSWAP reg */

            tmp8u = (opcode&7)+(rex.b<<3);

            if(rex.w)

                emu->regs[tmp8u].q[0] = __builtin_bswap64(emu->regs[tmp8u].q[0]);

            else

                emu->regs[tmp8u].q[0] = __builtin_bswap32(emu->regs[tmp8u].dword[0]);

            break;



        case 0xD1:                   /* PSRLW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            if(EM->q>15)

                GM->q = 0;

            else {

                tmp8u = EM->ub[0];

                for(int i=0; i<4; ++i)

                    GM->uw[i] >>= tmp8u;

            }

            break;

        case 0xD2:                   /* PSRLD Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            if(EM->q>31)

                GM->q=0;

            else {

                tmp8u = EM->ub[0];

                for(int i=0; i<2; ++i)

                    GM->ud[i] >>= tmp8u;

            }

            break;

        case 0xD3:                   /* PSRLQ Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->q = (EM->q > 63) ? 0L : (GM->q >> EM->q);

            break;

        case 0xD4:                   /* PADDQ Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->sq += EM->sq;

            break;

        case 0xD5:                   /* PMULLW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i) {

                tmp32s = (int32_t)GM->sw[i] * EM->sw[i];

                GM->sw[i] = tmp32s;

            }

            break;



        case 0xD7:                   /* PMOVMSKB Gd,Em */

            nextop = F8;

            GETEM(0);

            GETGD;

            GD->q[0] = 0;

            for (int i=0; i<8; ++i)

                if(EM->ub[i]&0x80)

                    GD->dword[0] |= (1<<i);

            break;

        case 0xD8:                   /* PSUBUSB Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<8; ++i) {

                tmp32s = (int32_t)GM->ub[i] - EM->ub[i];

                GM->ub[i] = (tmp32s < 0) ? 0 : tmp32s;

            }

            break;

        case 0xD9:                   /* PSUBUSW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i) {

                tmp32s = (int32_t)GM->uw[i] - EM->uw[i];

                GM->uw[i] = (tmp32s < 0) ? 0 : tmp32s;

            }

            break;

        case 0xDA:                   /* PMINUB Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i=0; i<8; ++i)

                GM->ub[i] = (GM->ub[i]<EM->ub[i])?GM->ub[i]:EM->ub[i];

            break;

        case 0xDB:                   /* PAND Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->q &= EM->q;

            break;

        case 0xDC:                   /* PADDUSB Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<8; ++i) {

                tmp32u = (uint32_t)GM->ub[i] + EM->ub[i];

                GM->ub[i] = (tmp32u>255) ? 255 : tmp32u;

            }

            break;

        case 0xDD:                   /* PADDUSW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i) {

                tmp32u = (uint32_t)GM->uw[i] + EM->uw[i];

                GM->uw[i] = (tmp32u>65535) ? 65535 : tmp32u;

            }

            break;

        case 0xDE:                   /* PMAXUB Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i=0; i<8; ++i)

                GM->ub[i] = (GM->ub[i]>EM->ub[i])?GM->ub[i]:EM->ub[i];

            break;

        case 0xDF:                   /* PANDN Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->q = (~GM->q) & EM->q;

            break;

        case 0xE0:                   /* PAVGB Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<8; ++i)

                    GM->ub[i] = ((uint32_t)GM->ub[i]+EM->ub[i]+1)>>1;

            break;

        case 0xE1:                   /* PSRAW Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            if(EM->q>15)

                tmp8u = 15;

            else

                tmp8u = EM->ub[0];

            for(int i=0; i<4; ++i)

                GM->sw[i] >>= tmp8u;

            break;

        case 0xE2:                   /* PSRAD Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            if(EM->q>31) {

                for(int i=0; i<2; ++i)

                    GM->sd[i] = (GM->sd[i]<0)?-1:0;

            } else {

                tmp8u = EM->ub[0];

                for(int i=0; i<2; ++i)

                    GM->sd[i] >>= tmp8u;

            }

            break;

        case 0xE3:                   /* PAVGW Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i)

                GM->uw[i] = ((uint32_t)GM->uw[i]+EM->uw[i]+1)>>1;

            break;

        case 0xE4:                   /* PMULHUW Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i) {

                tmp32u = (uint32_t)GM->uw[i] * (uint32_t)EM->uw[i];

                GM->uw[i] = (tmp32u>>16)&0xffff;

            }

            break;

        case 0xE5:                   /* PMULHW Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i) {

                tmp32s = (int32_t)GM->sw[i] * (int32_t)EM->sw[i];

                GM->sw[i] = (tmp32s>>16)&0xffff;

            }

            break;

        case 0xE7:                   /* MOVNTQ Em,Gm */

            nextop = F8;

            if(MODREG)

                return 0;

            GETEM(0);

            GETGM;

            EM->q = GM->q;

            break;

        case 0xE8:                   /* PSUBSB Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<8; ++i) {

                tmp32s = (int32_t)GM->sb[i] - (int32_t)EM->sb[i];

                GM->sb[i] = (tmp32s>127)?127:((tmp32s<-128)?-128:tmp32s);

            }

            break;

        case 0xE9:                   /* PSUBSW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i) {

                tmp32s = (int32_t)GM->sw[i] - EM->sw[i];

                GM->sw[i] = (tmp32s>32767)?32767:((tmp32s<-32768)?-32768:tmp32s);

            }

            break;

        case 0xEA:                  /* PMINSW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i=0; i<4; ++i)

                GM->sw[i] = (GM->sw[i]<EM->sw[i])?GM->sw[i]:EM->sw[i];

            break;

        case 0xEB:                   /* POR Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->q |= EM->q;

            break;

        case 0xEC:                   /* PADDSB Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<8; ++i) {

                tmp32s = (int32_t)GM->sb[i] + EM->sb[i];

                GM->sb[i] = (tmp32s>127)?127:((tmp32s<-128)?-128:tmp32s);

            }

            break;

        case 0xED:                   /* PADDSW Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i) {

                tmp32s = (int32_t)GM->sw[i] + EM->sw[i];

                GM->sw[i] = (tmp32s>32767)?32767:((tmp32s<-32768)?-32768:tmp32s);

            }

            break;

        case 0xEE:                  /* PMAXSW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i=0; i<4; ++i)

                GM->sw[i] = (GM->sw[i]>EM->sw[i])?GM->sw[i]:EM->sw[i];

            break;

        case 0xEF:                   /* PXOR Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->q ^= EM->q;

            break;



        case 0xF1:                   /* PSLLW Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            if(EM->q>15)

                GM->q = 0;

            else {

                tmp8u = EM->ub[0];

                for(int i=0; i<4; ++i)

                    GM->sw[i] <<= tmp8u;

            }

            break;

        case 0xF2:                   /* PSLLD Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            if(EM->q>31)

                GM->q = 0;

            else {

                tmp8u = EM->ub[0];

                for(int i=0; i<2; ++i)

                    GM->sd[i] <<= tmp8u;

            }

            break;

        case 0xF3:                   /* PSLLQ Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->q = (EM->q > 63) ? 0L : (GM->q << EM->ub[0]);

            break;

        case 0xF4:                   /* PMULUDQ Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->q = (uint64_t)GM->ud[0] * (uint64_t)EM->ud[0];

            break;

        case 0xF5:                   /* PMADDWD Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i=0; i<2; ++i) {

                int offset = i * 2;

                tmp32s = (int32_t)GM->sw[offset + 0] * EM->sw[offset + 0];

                tmp32s2 = (int32_t)GM->sw[offset + 1] * EM->sw[offset + 1];

                GM->sd[i] = tmp32s + tmp32s2;

            }

            break;

        case 0xF6:                   /* PSADBW Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            tmp32u = 0;

            for (int i=0; i<8; ++i)

                tmp32u += (GM->ub[i]>EM->ub[i])?(GM->ub[i] - EM->ub[i]):(EM->ub[i] - GM->ub[i]);

            GM->q = tmp32u;

            break;

        case 0xF7:                   /* MASKMOVQ Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for (int i = 0; i < 8; i++) {

                if (EM->ub[i] & 0x80) {

                   ((reg64_t*)(emu->regs[_DI].q[0]))->byte[i] = GM->ub[i];

                }

            }

            break;

        case 0xF8:                   /* PSUBB Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<8; ++i)

                GM->ub[i] -= EM->ub[i];

            break;

        case 0xF9:                   /* PSUBW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i)

                GM->uw[i] -= EM->uw[i];

            break;

        case 0xFA:                   /* PSUBD Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<2; ++i)

                GM->ud[i] -= EM->ud[i];

            break;

        case 0xFB:                   /* PSUBQ Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            GM->sq -= EM->sq;

            break;

        case 0xFC:                   /* PADDB Gm, Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<8; ++i)

                GM->ub[i] += EM->ub[i];

            break;

        case 0xFD:                   /* PADDW Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<4; ++i)

                GM->uw[i] += EM->uw[i];

            break;

        case 0xFE:                   /* PADDD Gm,Em */

            nextop = F8;

            GETEM(0);

            GETGM;

            for(int i=0; i<2; ++i)

                GM->ud[i] += EM->ud[i];

            break;



        default:

            return 0;

    }

    return addr;

}