#define _GNU_SOURCE

#include <stdint.h>

#include <stdio.h>

#include <stdlib.h>

#include <math.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 "bridge.h"



#include "modrm.h"



#ifdef TEST_INTERPRETER

uintptr_t Test64(x64test_t *test, rex_t rex, int seg, uintptr_t addr)

#else

uintptr_t Run64(x64emu_t *emu, rex_t rex, int seg, uintptr_t addr)

#endif

{

    uint8_t opcode;

    uint8_t nextop;

    uint8_t tmp8u;   (void)tmp8u;

    int16_t tmp16s;  (void)tmp16s;

    uint16_t tmp16u; (void)tmp16u;

    int32_t tmp32s;

    uint32_t tmp32u;

    uint64_t tmp64u;

    reg64_t *oped, *opgd;

    sse_regs_t *opex, *opgx;

    int rep;

    #ifdef TEST_INTERPRETER

    x64emu_t* emu = test->emu;

    #endif

    uintptr_t tlsdata = GetSegmentBaseEmu(emu, seg);



    opcode = F8;

    // REX prefix before the F0 are ignored

    rex.rex = 0;

    if(!rex.is32bits)

        while(opcode>=0x40 && opcode<=0x4f) {

            rex.rex = opcode;

            opcode = F8;

        }

    rep = 0;

    while((opcode==0xF2) || (opcode==0xF3)) {

        rep = opcode-0xF1;

        opcode = F8;

    }

    if(!rex.is32bits)

        while(opcode>=0x40 && opcode<=0x4f) {

            rex.rex = opcode;

            opcode = F8;

        }



    switch(opcode) {

        #define GO(B, OP)                                   \

        case B+0:                                           \

            nextop = F8;                                    \

            GETEB_OFFS(0, tlsdata);                         \

            GETGB;                                          \

            EB->byte[0] = OP##8(emu, EB->byte[0], GB);      \

            break;                                          \

        case B+1:                                           \

            nextop = F8;                                    \

            GETED_OFFS(0, tlsdata);                         \

            GETGD;                                          \

            if(rex.w)                                       \

                ED->q[0] = OP##64(emu, ED->q[0], GD->q[0]); \

            else {                                          \

                if(MODREG)                                  \

                    ED->q[0] = OP##32(emu, ED->dword[0], GD->dword[0]);     \

                else                                                        \

                    ED->dword[0] = OP##32(emu, ED->dword[0], GD->dword[0]); \

            }                                               \

            break;                                          \

        case B+2:                                           \

            nextop = F8;                                    \

            GETEB_OFFS(0, tlsdata);                         \

            GETGB;                                          \

            GB = OP##8(emu, GB, EB->byte[0]);               \

            break;                                          \

        case B+3:                                           \

            nextop = F8;                                    \

            GETED_OFFS(0, tlsdata);                         \

            GETGD;                                          \

            if(rex.w)                                       \

                GD->q[0] = OP##64(emu, GD->q[0], ED->q[0]); \

            else                                            \

                GD->q[0] = OP##32(emu, GD->dword[0], ED->dword[0]); \

            break;                                          \

        case B+4:                                           \

            R_AL = OP##8(emu, R_AL, F8);                    \

            break;                                          \

        case B+5:                                           \

            if(rex.w)                                       \

                R_RAX = OP##64(emu, R_RAX, F32S64);         \

            else                                            \

                R_RAX = OP##32(emu, R_EAX, F32);            \

            break;



        GO(0x00, add)                   /* ADD 0x00 -> 0x05 */

        GO(0x08, or)                    /*  OR 0x08 -> 0x0D */

        GO(0x10, adc)                   /* ADC 0x10 -> 0x15 */

        GO(0x18, sbb)                   /* SBB 0x18 -> 0x1D */

        GO(0x20, and)                   /* AND 0x20 -> 0x25 */

        GO(0x28, sub)                   /* SUB 0x28 -> 0x2D */

        GO(0x30, xor)                   /* XOR 0x30 -> 0x35 */

        #undef GO

        case 0x0F:

            opcode = F8;

            switch(opcode) {



                case 0x10:

                    switch(rep) {

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

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

                            GX->u128 = EX->u128;

                            break;

                        case 1: /* MOVSD Gx, FS:Ex */

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

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

                            if(!MODREG) {

                                // EX is not a register

                                GX->q[1] = 0;

                            }

                            break;

                        case 2: /* MOVSS Gx, FS:Ex */

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

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

                            if(!MODREG) {

                                // EX is not a register (reg to reg only move 31:0)

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

                            }

                            break;

                        default:

                            return 0;

                    }

                    break;

                case 0x11:

                    switch(rep) {

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

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

                            EX->u128 = GX->u128;

                            break;

                        case 1: /* MOVSD Ex, Gx */

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

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

                            break;

                        case 2: /* MOVSS FS:Ex, Gx */

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

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

                            break;

                        default:

                            return 0;

                    }

                    break;

                case 0x28:

                    switch(rep) {

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

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

                            GX->u128 = EX->u128;

                            break;

                        default:

                            return 0;

                    }

                    break;

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

                    switch(rep) {

                        case 0:

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

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

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

                            break;

                        default:

                            return 0;

                    }

                    break;



                case 0x58:

                    switch(rep) {

                        case 2: /* ADDSS Gx, FS:Ex */

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

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

                            break;



                        default:

                            return 0;

                    }

                    break;

                case 0x59:

                    switch(rep) {

                        case 2: /* MULSS Gx, FS:Ex */

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

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

                            break;



                        default:

                            return 0;

                    }

                    break;

                case 0x5A:

                    switch(rep) {

                        case 2:  /* CVTSS2SD Gx, FS:Ex */

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

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

                            break;



                        default:

                            return 0;

                    }

                    break;



                case 0x6F:

                    switch(rep) {

                        case 2: /* MOVDQU Gx, FS:Ex */

                            nextop = F8;

                            GETEX_OFFS(0, tlsdata);

                            GETGX;

                            memcpy(GX, EX, 16);    // unaligned...

                            break;

                            

                        default:

                            return 0;

                    }

                    break;



                case 0xAF:

                    switch(rep) {

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

                            nextop = F8;

                            GETED_OFFS(0, tlsdata);

                            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;

                        default:

                            return 0;

                    }

                    break;



                case 0xB6:

                    switch(rep) {

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

                            nextop = F8;

                            GETEB_OFFS(0, tlsdata);

                            GETGD;

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

                            break;

                        default:

                            return 0;

                    }

                    break;

                case 0xB7:

                    switch(rep) {

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

                            nextop = F8;

                            GETEW_OFFS(0, tlsdata);

                            GETGD;

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

                            break;

                        default:

                            return 0;

                    }

                    break;



                case 0xBA:                      

                    nextop = F8;

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

                        case 4:                 /* BT Ed,Ib */

                            CHECK_FLAGS(emu);

                            GETED_OFFS(1, tlsdata);

                            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_OFFS(1, tlsdata);

                            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_OFFS(1, tlsdata);

                            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_OFFS(1, tlsdata);

                            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;



                default:

                    return 0;

            }

            break;



        case 0x38:

            nextop = F8;

            GETEB_OFFS(0, tlsdata);

            GETGB;

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

            break;

        case 0x39:

            nextop = F8;

            GETED_OFFS(0, tlsdata);

            GETGD;

            if(rex.w)

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

            else

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

            break;



        case 0x3B:

            nextop = F8;

            GETED_OFFS(0, tlsdata);

            GETGD;

            if(rex.w)

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

            else

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

            break;





        case 0x63:                      /* MOVSXD Gd, FS:Ed */

            nextop = F8;

            GETED_OFFS(0, tlsdata);

            GETGD;

            if(rex.is32bits) {

                // ARPL here

                // faking to always happy...

                SET_FLAG(F_ZF);

            } else {

                if(rex.w)

                    GD->sq[0] = ED->sdword[0];

                else

                    if(MODREG)

                        GD->q[0] = ED->dword[0];    // not really a sign extension

                    else

                        GD->sdword[0] = ED->sdword[0];  // meh?

            }

            break;

        case 0x64:                      /* FS: prefix */

            #ifdef TEST_INTERPRETER

            return Test64(test, rex, _FS, addr);

            #else

            return Run64(emu, rex, _FS, addr);

            #endif

            break;

        case 0x65:                      /* GS: prefix */

            #ifdef TEST_INTERPRETER

            return Test64(test, rex, _GS, addr);

            #else

            return Run64(emu, rex, _GS, addr);

            #endif

            break;

        case 0x66:

            return Run6664(emu, rex, seg, addr);

        case 0x67:

            if(rex.is32bits)

                return Run6764_32(emu, rex, seg, seg, addr);

            else

                return 0;



        case 0x80:                      /* GRP Eb,Ib */

            nextop = F8;

            GETEB_OFFS(1, tlsdata);

            tmp8u = F8;

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

                case 0: EB->byte[0] = add8(emu, EB->byte[0], tmp8u); break;

                case 1: EB->byte[0] =  or8(emu, EB->byte[0], tmp8u); break;

                case 2: EB->byte[0] = adc8(emu, EB->byte[0], tmp8u); break;

                case 3: EB->byte[0] = sbb8(emu, EB->byte[0], tmp8u); break;

                case 4: EB->byte[0] = and8(emu, EB->byte[0], tmp8u); break;

                case 5: EB->byte[0] = sub8(emu, EB->byte[0], tmp8u); break;

                case 6: EB->byte[0] = xor8(emu, EB->byte[0], tmp8u); break;

                case 7:               cmp8(emu, EB->byte[0], tmp8u); break;

            }

            break;

        case 0x81:                      /* GRP Ed,Id */

        case 0x83:                      /* GRP Ed,Ib */

            nextop = F8;

            GETED_OFFS((opcode==0x81)?4:1, tlsdata);

            if(opcode==0x81) {

                tmp32s = F32S;

            } else {

                tmp32s = F8S;

            }

            if(rex.w) {

                tmp64u = (uint64_t)(int64_t)tmp32s;

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

                    case 0: ED->q[0] = add64(emu, ED->q[0], tmp64u); break;

                    case 1: ED->q[0] =  or64(emu, ED->q[0], tmp64u); break;

                    case 2: ED->q[0] = adc64(emu, ED->q[0], tmp64u); break;

                    case 3: ED->q[0] = sbb64(emu, ED->q[0], tmp64u); break;

                    case 4: ED->q[0] = and64(emu, ED->q[0], tmp64u); break;

                    case 5: ED->q[0] = sub64(emu, ED->q[0], tmp64u); break;

                    case 6: ED->q[0] = xor64(emu, ED->q[0], tmp64u); break;

                    case 7:            cmp64(emu, ED->q[0], tmp64u); break;

                }

            } else {

                tmp32u = (uint32_t)tmp32s;

                if(MODREG)

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

                        case 0: ED->q[0] = add32(emu, ED->dword[0], tmp32u); break;

                        case 1: ED->q[0] =  or32(emu, ED->dword[0], tmp32u); break;

                        case 2: ED->q[0] = adc32(emu, ED->dword[0], tmp32u); break;

                        case 3: ED->q[0] = sbb32(emu, ED->dword[0], tmp32u); break;

                        case 4: ED->q[0] = and32(emu, ED->dword[0], tmp32u); break;

                        case 5: ED->q[0] = sub32(emu, ED->dword[0], tmp32u); break;

                        case 6: ED->q[0] = xor32(emu, ED->dword[0], tmp32u); break;

                        case 7:            cmp32(emu, ED->dword[0], tmp32u); break;

                    }

                else

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

                        case 0: ED->dword[0] = add32(emu, ED->dword[0], tmp32u); break;

                        case 1: ED->dword[0] =  or32(emu, ED->dword[0], tmp32u); break;

                        case 2: ED->dword[0] = adc32(emu, ED->dword[0], tmp32u); break;

                        case 3: ED->dword[0] = sbb32(emu, ED->dword[0], tmp32u); break;

                        case 4: ED->dword[0] = and32(emu, ED->dword[0], tmp32u); break;

                        case 5: ED->dword[0] = sub32(emu, ED->dword[0], tmp32u); break;

                        case 6: ED->dword[0] = xor32(emu, ED->dword[0], tmp32u); break;

                        case 7:                cmp32(emu, ED->dword[0], tmp32u); break;

                    }

            }

            break;



        case 0x86:                      /* XCHG Eb,Gb */

            nextop = F8;

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

            GETEB_OFFS(0, tlsdata);

            GETGB;

            if(MODREG) { // reg / reg: no lock

                tmp8u = GB;

                GB = EB->byte[0];

                EB->byte[0] = tmp8u;

            } else {

                GB = native_lock_xchg_b(EB, GB);

            }

            // dynarec use need it's own mecanism

#else

            GETEB_OFFS(0, tlsdata);

            GETGB;

            if(!MODREG)

                pthread_mutex_lock(&my_context->mutex_lock); // XCHG always LOCK (but when accessing memory only)

            tmp8u = GB;

            GB = EB->byte[0];

            EB->byte[0] = tmp8u;

            if(!MODREG)

                pthread_mutex_unlock(&my_context->mutex_lock);

#endif                

            break;



        case 0x88:                      /* MOV FS:Eb,Gb */

            nextop = F8;

            GETEB_OFFS(0, tlsdata);

            GETGB;

            EB->byte[0] = GB;

            break;

        case 0x89:                    /* MOV FS:Ed,Gd */

            nextop = F8;

            GETED_OFFS(0, tlsdata);

            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 0x8A:                      /* MOV Gb, FS:Eb */

            nextop = F8;

            GETEB_OFFS(0, tlsdata);

            GETGB;

            GB = EB->byte[0];

            break;

        case 0x8B:                      /* MOV Gd, FS:Ed */

            nextop = F8;

            GETED_OFFS(0, tlsdata);

            GETGD;

            if(rex.w)

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

            else

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

            break;



        case 0x8D:                      /* LEA Gd,M */

            nextop = F8;

            GETGD;

            tmp64u = GETEA(0);

            if(rex.w)

                GD->q[0] = tmp64u;

            else

                GD->q[0] = tmp64u&0xffffffff;

            break;

        case 0x8E:                      /* MOV Seg, Seg:Ew */

            nextop = F8;

            GETED_OFFS(0, tlsdata);

            emu->segs[((nextop&0x38)>>3)] = ED->word[0];

            emu->segs_serial[((nextop&0x38)>>3)] = 0;

            break;

        case 0x8F:                      /* POP FS:Ed */

            nextop = F8;

            if(MODREG) {

                emu->regs[(nextop&7)+(rex.b<<3)].q[0] = Pop64(emu);

            } else {

                if(rex.is32bits) {

                    tmp32u = Pop32(emu);  // this order allows handling POP [ESP] and variant

                    GETED_OFFS(0, tlsdata);

                    R_ESP -= 4; // to prevent issue with SEGFAULT

                    ED->dword[0] = tmp32u;

                    R_ESP += 4;

                } else {

                    tmp64u = Pop64(emu);  // this order allows handling POP [ESP] and variant

                    GETED_OFFS(0, tlsdata);

                    R_RSP -= sizeof(void*); // to prevent issue with SEGFAULT

                    ED->q[0] = tmp64u;

                    R_RSP += sizeof(void*);

                }

            }

            break;

        case 0x90:                      /* NOP */

            break;



        case 0xA1:                      /* MOV EAX,FS:Od */

            if(rex.is32bits) {

                tmp32s = F32S;

                R_EAX = *(uint32_t*)(tlsdata+tmp32s);

            } else {

                tmp64u = F64;

                if(rex.w)

                    R_RAX = *(uint64_t*)(tlsdata+tmp64u);

                else

                    R_RAX = *(uint32_t*)(tlsdata+tmp64u);

            }

            break;



        case 0xA3:                      /* MOV FS:Od,EAX */

            if(rex.is32bits) {

                tmp32s = F32S;

                *(uint32_t*)(uintptr_t)(tlsdata+tmp32s) = R_EAX;

            } else {

                tmp64u = F64;

                if(rex.w)

                    *(uint64_t*)(tlsdata+tmp64u) = R_RAX;

                else

                    *(uint32_t*)(tlsdata+tmp64u) = R_EAX;

            }

            break;



        case 0xC6:                      /* MOV FS:Eb, Ib */

            nextop = F8;

            GETEB_OFFS(1, tlsdata);

            EB->byte[0] = F8;

            break;

        case 0xC7:                      /* MOV FS:Ed, Id */

            nextop = F8;

            GETED_OFFS(4, tlsdata);

            if(rex.w)

                ED->q[0] = F32S64;

            else

                if(MODREG)

                    ED->q[0] = F32;

                else

                    ED->dword[0] = F32;

            break;



        case 0xD1:                      /* GRP2 Ed,1 */

        case 0xD3:                      /* GRP2 Ed,CL */

            nextop = F8;

            GETED_OFFS(0, tlsdata);

            tmp8u = (opcode==0xD1)?1:R_CL;

            if(rex.w) {

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

                    case 0: ED->q[0] = rol64(emu, ED->q[0], tmp8u); break;

                    case 1: ED->q[0] = ror64(emu, ED->q[0], tmp8u); break;

                    case 2: ED->q[0] = rcl64(emu, ED->q[0], tmp8u); break;

                    case 3: ED->q[0] = rcr64(emu, ED->q[0], tmp8u); break;

                    case 4: 

                    case 6: ED->q[0] = shl64(emu, ED->q[0], tmp8u); break;

                    case 5: ED->q[0] = shr64(emu, ED->q[0], tmp8u); break;

                    case 7: ED->q[0] = sar64(emu, ED->q[0], tmp8u); break;

                }

            } else {

                if(MODREG)

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

                        case 0: ED->q[0] = rol32(emu, ED->dword[0], tmp8u); break;

                        case 1: ED->q[0] = ror32(emu, ED->dword[0], tmp8u); break;

                        case 2: ED->q[0] = rcl32(emu, ED->dword[0], tmp8u); break;

                        case 3: ED->q[0] = rcr32(emu, ED->dword[0], tmp8u); break;

                        case 4: 

                        case 6: ED->q[0] = shl32(emu, ED->dword[0], tmp8u); break;

                        case 5: ED->q[0] = shr32(emu, ED->dword[0], tmp8u); break;

                        case 7: ED->q[0] = sar32(emu, ED->dword[0], tmp8u); break;

                    }

                else

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

                        case 0: ED->dword[0] = rol32(emu, ED->dword[0], tmp8u); break;

                        case 1: ED->dword[0] = ror32(emu, ED->dword[0], tmp8u); break;

                        case 2: ED->dword[0] = rcl32(emu, ED->dword[0], tmp8u); break;

                        case 3: ED->dword[0] = rcr32(emu, ED->dword[0], tmp8u); break;

                        case 4: 

                        case 6: ED->dword[0] = shl32(emu, ED->dword[0], tmp8u); break;

                        case 5: ED->dword[0] = shr32(emu, ED->dword[0], tmp8u); break;

                        case 7: ED->dword[0] = sar32(emu, ED->dword[0], tmp8u); break;

                    }

            }

            break;



        case 0xEB:                      /* JMP Ib */

            tmp32s = F8S; // jump is relative

            addr += tmp32s;

            break;



        case 0xF7:                      /* GRP3 Ed(,Id) */

            nextop = F8;

            tmp8u = (nextop>>3)&7;

            GETED_OFFS((tmp8u<2)?4:0, tlsdata);

            if(rex.w) {

                switch(tmp8u) {

                    case 0: 

                    case 1:                 /* TEST Ed,Id */

                        tmp64u = F32S64;

                        test64(emu, ED->q[0], tmp64u);

                        break;

                    case 2:                 /* NOT Ed */

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

                        break;

                    case 3:                 /* NEG Ed */

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

                        break;

                    case 4:                 /* MUL RAX,Ed */

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

                        break;

                    case 5:                 /* IMUL RAX,Ed */

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

                        break;

                    case 6:                 /* DIV Ed */

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

                        break;

                    case 7:                 /* IDIV Ed */

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

                        #ifdef TEST_INTERPRETER

                        test->notest = 1;

                        #endif

                        break;

                }

            } else {

                switch(tmp8u) {

                    case 0: 

                    case 1:                 /* TEST Ed,Id */

                        tmp32u = F32;

                        test32(emu, ED->dword[0], tmp32u);

                        break;

                    case 2:                 /* NOT Ed */

                        if(MODREG)

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

                        else

                            ED->dword[0] = not32(emu, ED->dword[0]);

                        break;

                    case 3:                 /* NEG Ed */

                        if(MODREG)

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

                        else

                            ED->dword[0] = neg32(emu, ED->dword[0]);

                        break;

                    case 4:                 /* MUL EAX,Ed */

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

                        emu->regs[_AX].dword[1] = 0;

                        break;

                    case 5:                 /* IMUL EAX,Ed */

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

                        emu->regs[_AX].dword[1] = 0;

                        break;

                    case 6:                 /* DIV Ed */

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

                        //emu->regs[_AX].dword[1] = 0;  // already put high regs to 0

                        //emu->regs[_DX].dword[1] = 0;

                        break;

                    case 7:                 /* IDIV Ed */

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

                        //emu->regs[_AX].dword[1] = 0;

                        //emu->regs[_DX].dword[1] = 0;

                        break;

                }

            }

            break;

            

        case 0xFF:                      /* GRP 5 Ed */

            nextop = F8;

            GETED_OFFS(0, tlsdata);

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

                case 0:                 /* INC Ed */

                    if(rex.w)

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

                    else {

                        if(MODREG)

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

                        else

                            ED->dword[0] = inc32(emu, ED->dword[0]);

                    }

                    break;

                case 1:                 /* DEC Ed */

                    if(rex.w)

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

                    else {

                        if(MODREG)

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

                        else

                            ED->dword[0] = dec32(emu, ED->dword[0]);

                    }

                    break;

                case 2:                 /* CALL NEAR Ed */

                    if(rex.is32bits) {

                        tmp64u = (uintptr_t)getAlternate((void*)(uintptr_t)ED->dword[0]);

                        Push32(emu, addr);

                    } else {

                        tmp64u = (uintptr_t)getAlternate((void*)ED->q[0]);

                        Push64(emu, addr);

                    }

                    addr = tmp64u;

                    break;

                case 3:                 /* CALL FAR Ed */

                    if(MODREG) {

                        printf_log(LOG_NONE, "Illegal Opcode %p: %02X %02X %02X %02X\n", (void*)R_RIP, opcode, nextop, PK(2), PK(3));

                        emu->quit=1;

                        emu->error |= ERR_ILLEGAL;

                        return 0;

                    } else {

                        if(rex.is32bits || !rex.w) {

                            Push32(emu, R_CS);

                            Push32(emu, addr);

                            R_RIP = addr = ED->dword[0];

                            R_CS = ED->word[2];

                        } else {

                            Push64(emu, R_CS);

                            Push64(emu, addr);

                            R_RIP = addr = ED->q[0];

                            R_CS = (ED+1)->word[0];

                        }

                        return 0;  // exit loop to recompute new CS...

                    }

                    break;

                case 4:                 /* JMP NEAR Ed */

                    if(rex.is32bits)

                        addr = (uintptr_t)getAlternate((void*)(uintptr_t)ED->dword[0]);

                    else

                        addr = (uintptr_t)getAlternate((void*)ED->q[0]);

                    break;

                case 5:                 /* JMP FAR Ed */

                    if(MODREG) {

                        printf_log(LOG_NONE, "Illegal Opcode %p: 0x%02X 0x%02X %02X %02X\n", (void*)R_RIP, opcode, nextop, PK(2), PK(3));

                        emu->quit=1;

                        emu->error |= ERR_ILLEGAL;

                        return 0;

                    } else {

                        if(rex.is32bits || !rex.w) {

                            R_RIP = addr = ED->dword[0];

                            R_CS = ED->word[2];

                        } else {

                            R_RIP = addr = ED->q[0];

                            R_CS = (ED+1)->word[0];

                        }

                    }

                    break;

                case 6:                 /* Push Ed */

                    if(rex.is32bits) {

                        tmp32u = ED->dword[0];

                        Push32(emu, tmp32u);  // avoid potential issue with push [esp+...]

                    } else {

                        tmp64u = ED->q[0];  // rex.w ignored

                        Push64(emu, tmp64u);  // avoid potential issue with push [esp+...]

                    }

                    break;

                default:

                    printf_log(LOG_NONE, "Illegal Opcode %p: %02X %02X %02X %02X %02X %02X\n",(void*)R_RIP, opcode, nextop, PK(2), PK(3), PK(4), PK(5));

                    emu->quit=1;

                    emu->error |= ERR_ILLEGAL;

                    return 0;

            }

            break;

        default:

            return 0;

    }

    return addr;

}