#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);
                        #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);
                    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);
            #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);
            #endif
            break;

        case 0x0B:                      /* UD2 */
            #ifndef TEST_INTERPRETER
            emit_signal(emu, SIGILL, (void*)R_RIP, 0);
            #endif
            break;

        case 0x0D:
            nextop = F8;
            GETED(0);
            switch((nextop>>3)&7) {
                case 0: //PREFETCH?
                    __builtin_prefetch((void*)ED, 0, 0);
                    break;
                case 1: //PREFETCHW
                    __builtin_prefetch((void*)ED, 1, 0);
                    break;
                case 2: //PREFETCHWT1
                    __builtin_prefetch((void*)ED, 1, 0);
                    break;
                default:    //NOP
                    break;
            }
            break;
        case 0x0E:                      /* FEMMS */
            #ifndef TEST_INTERPRETER
            emit_signal(emu, SIGILL, (void*)R_RIP, 0);
            #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 0x30:                      /* WRMSR */
            // this is a privilege opcode...
            #ifndef TEST_INTERPRETER
            emit_signal(emu, SIGSEGV, (void*)R_RIP, 0);
            #endif
            break;
        case 0x31:                   /* RDTSC */
            tmp64u = ReadTSC(emu);
            if(box64_rdtsc_shift)
                tmp64u<<=box64_rdtsc_shift;
            R_RDX = tmp64u>>32;
            R_RAX = tmp64u&0xFFFFFFFF;
            break;
        case 0x32:                   /* RDMSR */
            // priviledge instruction
            #ifndef TEST_INTERPRETER
            emit_signal(emu, SIGSEGV, (void*)R_RIP, 0xbad0);
            STEP;
            #endif
            break;

        case 0x34:                  /* SYSENTER */
            #ifndef TEST_INTERPRETER
            emit_signal(emu, SIGSEGV, (void*)R_RIP, 0xbad0);
            STEP;
            #endif
            break;
        case 0x35:                  /* SYSEXIT */
            #ifndef TEST_INTERPRETER
            emit_signal(emu, SIGSEGV, (void*)R_RIP, 0xbad0);
            STEP;
            #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]);
            }
            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];
            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);
            , 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);
            break;
        case 0xA3:                      /* BT 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);
            } 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.is32bits)
                        fpu_fxsave32(emu, ED);
                    else
                        fpu_fxsave64(emu, ED);
                    #endif
                    break;
                case 1:                 /* FXRSTOR Ed */
                    _GETED(0);
                    if(rex.is32bits)
                        fpu_fxrstor32(emu, ED);
                    else
                        fpu_fxrstor64(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.w?0:1);
                    #endif
                    break;
                case 5:                 /* XRSTOR Ed */
                    _GETED(0);
                    fpu_xrstor(emu, ED, rex.w?0:1);
                    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;
}
