#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"
#ifdef DYNAREC
#include "../dynarec/native_lock.h"
#endif

#include "modrm.h"

#ifdef TEST_INTERPRETER
uintptr_t Test66(x64test_t *test, rex_t rex, int rep, uintptr_t addr)
#else
uintptr_t Run66(x64emu_t *emu, rex_t rex, int rep, uintptr_t addr)
#endif
{
    uint8_t opcode;
    uint8_t nextop;
    int8_t tmp8s;
    uint8_t tmp8u, tmp8u2;
    int16_t tmp16s;
    uint16_t tmp16u, tmp16u2;
    int32_t tmp32s;
    int64_t tmp64s;
    uint64_t tmp64u, tmp64u2, tmp64u3;
    reg64_t *oped, *opgd;
    #ifdef TEST_INTERPRETER
    x64emu_t* emu = test->emu;
    #endif

    opcode = F8;

    while((opcode==0x2E) || (opcode==0x36) || (opcode==0x26) || (opcode==0x66))   // ignoring CS:, SS:, ES: or multiple 0x66
        opcode = F8;

    while((opcode==0xF2) || (opcode==0xF3)) {
        rep = opcode-0xF1;
        opcode = F8;
    }

    rex.rex = 0;
    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(0);                                                   \
        GETGB;                                                      \
        EB->byte[0] = OP##8(emu, EB->byte[0], GB);                  \
        break;                                                      \
    case B+1:                                                       \
        nextop = F8;                                                \
        GETEW(0);                                                   \
        GETGW;                                                      \
        if(rex.w)                                                   \
            EW->q[0] = OP##64(emu, EW->q[0], GW->q[0]);             \
        else                                                        \
            EW->word[0] = OP##16(emu, EW->word[0], GW->word[0]);    \
        break;                                                      \
    case B+2:                                                       \
        nextop = F8;                                                \
        GETEB(0);                                                   \
        GETGB;                                                      \
        GB = OP##8(emu, GB, EB->byte[0]);                           \
        break;                                                      \
    case B+3:                                                       \
        nextop = F8;                                                \
        GETEW(0);                                                   \
        GETGW;                                                      \
        if(rex.w)                                                   \
            GW->q[0] = OP##64(emu, GW->q[0], EW->q[0]);             \
        else                                                        \
            GW->word[0] = OP##16(emu, GW->word[0], EW->word[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_AX = OP##16(emu, R_AX, F16);                          \
        break;

    GO(0x00, add)                   /* ADD 0x01 ~> 0x05 */
    GO(0x08, or)                    /*  OR 0x09 ~> 0x0D */
    GO(0x10, adc)                   /* ADC 0x11 ~> 0x15 */
    GO(0x18, sbb)                   /* SBB 0x19 ~> 0x1D */
    GO(0x20, and)                   /* AND 0x21 ~> 0x25 */
    GO(0x28, sub)                   /* SUB 0x29 ~> 0x2D */
    GO(0x30, xor)                   /* XOR 0x31 ~> 0x35 */

    case 0x06:                      /* PUSH ES */
        if(!rex.is32bits) {
            return 0;
        }
        Push16(emu, emu->segs[_ES]);
        break;
    case 0x07:                      /* POP ES */
        if(!rex.is32bits) {
            return 0;
        }
        emu->segs[_ES] = Pop16(emu);
        emu->segs_serial[_ES] = 0;
        break;

    case 0x0F:                              /* more opcdes */
        switch(rep) {
            case 0:
                #ifdef TEST_INTERPRETER
                return Test660F(test, rex, addr);
                #else
                return Run660F(emu, rex, addr);
                #endif
            case 1:
                #ifdef TEST_INTERPRETER
                return Test66F20F(test, rex, addr);
                #else
                return Run66F20F(emu, rex, addr);
                #endif
            case 2:
                #ifdef TEST_INTERPRETER
                return Test66F30F(test, rex, addr);
                #else
                return Run66F30F(emu, rex, addr);
                #endif
        }
        return 0; // unreachable

        case 0x1E:                      /* PUSH DS */
            if(!rex.is32bits) {
                return 0;
            }
            Push16(emu, emu->segs[_DS]);
            break;
        case 0x1F:                      /* POP DS */
            if(!rex.is32bits) {
                return 0;
            }
            emu->segs[_DS] = Pop16(emu);    // no check, no use....
            emu->segs_serial[_DS] = 0;
            break;

    case 0x39:
        nextop = F8;
        GETEW(0);
        GETGW;
        if(rex.w)
            cmp64(emu, EW->q[0], GW->q[0]);
        else
            cmp16(emu, EW->word[0], GW->word[0]);
        break;
    case 0x3B:
        nextop = F8;
        GETEW(0);
        GETGW;
        if(rex.w)
            cmp64(emu, GW->q[0], EW->dword[0]);
        else
            cmp16(emu, GW->word[0], EW->word[0]);
        break;
    case 0x3D:
        if(rex.w)
            cmp64(emu, R_RAX, F32S64);
        else
            cmp16(emu, R_AX, F16);
        break;

    case 0x40:
    case 0x41:
    case 0x42:
    case 0x43:
    case 0x44:
    case 0x45:
    case 0x46:
    case 0x47:                              /* INC Reg (32bits only) */
        tmp8u = opcode&7;
        emu->regs[tmp8u].word[0] = inc16(emu, emu->regs[tmp8u].word[0]);
        break;
    case 0x48:
    case 0x49:
    case 0x4A:
    case 0x4B:
    case 0x4C:
    case 0x4D:
    case 0x4E:
    case 0x4F:                              /* DEC Reg (32bits only) */
        tmp8u = opcode&7;
        emu->regs[tmp8u].word[0] = dec16(emu, emu->regs[tmp8u].word[0]);
        break;
    case 0x50:
    case 0x51:
    case 0x52:
    case 0x53:
    case 0x54:
    case 0x55:
    case 0x56:
    case 0x57:                      /* PUSH Reg */
        tmp16u = emu->regs[opcode&7].word[0];
        Push16(emu, tmp16u);
        break;
    case 0x58:
    case 0x59:
    case 0x5A:
    case 0x5B:
    case 0x5C:                      /* POP ESP */
    case 0x5D:
    case 0x5E:
    case 0x5F:                      /* POP Reg */
        tmp8u = opcode&7;
        emu->regs[tmp8u].word[0] = Pop16(emu);
        break;
    case 0x60:                              /* PUSHA */
        if(rex.is32bits) {
            tmp16u = R_SP;
            Push16(emu, R_AX);
            Push16(emu, R_CX);
            Push16(emu, R_DX);
            Push16(emu, R_BX);
            Push16(emu, tmp16u);
            Push16(emu, R_BP);
            Push16(emu, R_SI);
            Push16(emu, R_DI);
        } else {
            return 0;
        }
        break;
    case 0x61:                              /* POPA */
        if(rex.is32bits) {
            R_DI = Pop16(emu);
            R_SI = Pop16(emu);
            R_BP = Pop16(emu);
            R_ESP+=2;   // POP ESP
            R_BX = Pop16(emu);
            R_DX = Pop16(emu);
            R_CX = Pop16(emu);
            R_AX = Pop16(emu);
        } else {
            return 0;
        }
        break;

    case 0x64:                              /* FS: */
        #ifdef TEST_INTERPRETER
        return Test6664(test, rex, _FS, addr);
        #else
        return Run6664(emu, rex, _FS, addr);
        #endif
    case 0x65:                              /* GS: */
        #ifdef TEST_INTERPRETER
        return Test6664(test, rex, _GS, addr);
        #else
        return Run6664(emu, rex, _GS, addr);
        #endif

    case 0x68:                       /* PUSH u16 */
        tmp16u = F16;
        Push16(emu, tmp16u);
        break;
    case 0x69:                      /* IMUL Gw,Ew,Iw */
        nextop = F8;
        GETEW(rex.w?4:2);
        GETGW;
        if(rex.w) {
            tmp64u = F32S64;
            GW->q[0] = imul64(emu, EW->q[0], tmp64u);
        } else {
            tmp16u = F16;
            GW->word[0] = imul16(emu, EW->word[0], tmp16u);
        }
        break;
    case 0x6A:                       /* PUSH u8 */
        tmp16s = F8S;
        Push16(emu, tmp16s);
        break;
    case 0x6B:                      /* IMUL Gw,Ew,Ib */
        nextop = F8;
        GETEW(1);
        GETGW;
        if(rex.w) {
            tmp64s = F8S;
            GW->q[0] = imul64(emu, EW->q[0], (uint64_t)tmp64s);
        } else {
            tmp16s = F8S;
            GW->word[0] = imul16(emu, EW->word[0], (uint16_t)tmp16s);
        }
        break;
    
    case 0x70:
    case 0x71:
    case 0x72:
    case 0x73:
    case 0x74:
    case 0x75:
    case 0x76:
    case 0x77:
    case 0x78:
    case 0x79:
    case 0x7a:
    case 0x7b:
    case 0x7c:
    case 0x7d:
    case 0x7e:
    case 0x7f:
        // just ignore the 66 (and maybe other) prefix and use regular conditional jump
        addr--;
        return addr;

    case 0x81:                              /* GRP3 Ew,Iw */
    case 0x83:                              /* GRP3 Ew,Ib */
        nextop = F8;
        GETEW((opcode==0x81)?2:1);
        GETGW;
        if(opcode==0x81) 
            tmp16u = F16;
        else {
            tmp16s = F8S;
            tmp16u = (uint16_t)tmp16s;
        }
        switch((nextop>>3)&7) {
            case 0: EW->word[0] = add16(emu, EW->word[0], tmp16u); break;
            case 1: EW->word[0] =  or16(emu, EW->word[0], tmp16u); break;
            case 2: EW->word[0] = adc16(emu, EW->word[0], tmp16u); break;
            case 3: EW->word[0] = sbb16(emu, EW->word[0], tmp16u); break;
            case 4: EW->word[0] = and16(emu, EW->word[0], tmp16u); break;
            case 5: EW->word[0] = sub16(emu, EW->word[0], tmp16u); break;
            case 6: EW->word[0] = xor16(emu, EW->word[0], tmp16u); break;
            case 7:               cmp16(emu, EW->word[0], tmp16u); break;
        }
        break;

    case 0x85:                              /* TEST Ew,Gw */
        nextop = F8;
        GETEW(0);
        GETGW;
        test16(emu, EW->word[0], GW->word[0]);
        break;

    case 0x87:                              /* XCHG Ew,Gw */
        nextop = F8;
        GETEW(0);
        GETGW;
        if(rex.w) {
            tmp64u = GW->q[0];
            GW->q[0] = EW->q[0];
            EW->q[0] = tmp64u;
        } else {
            tmp16u = GW->word[0];
            GW->word[0] = EW->word[0];
            EW->word[0] = tmp16u;
        }
        break;

    case 0x89:                              /* MOV Ew,Gw */
        nextop = F8;
        GETEW(0);
        GETGW;
        if(rex.w)
            EW->q[0] = GW->q[0];
        else
            EW->word[0] = GW->word[0];
        break;

    case 0x8B:                              /* MOV Gw,Ew */
        nextop = F8;
        GETEW(0);
        GETGW;
        if(rex.w)
            GW->q[0] = EW->q[0];
        else
            GW->word[0] = EW->word[0];
        break;
    case 0x8C:                      /* MOV Ed, Seg */
        nextop = F8;
        GETEW(0);
        if(rex.w)
            EW->q[0] = emu->segs[((nextop&0x38)>>3)];
        else
            EW->word[0] = emu->segs[((nextop&0x38)>>3)];
        break;
    case 0x8D:                              /* LEA Gw,M */
        nextop = F8;
        GETGW;
        tmp64u = GETEA(0);
        if(rex.w)
            GW->q[0] = tmp64u;
        else
            GW->word[0] = (uint16_t)tmp64u;
        break;
    case 0x8E:                               /* MOV Seg,Ew */
        nextop = F8;
        GETEW(0);
        emu->segs[((nextop&0x38)>>3)] = EW->word[0];
        emu->segs_serial[((nextop&0x38)>>3)] = 0;
        break;
    case 0x8F:                              /* POP Ew */
        nextop = F8;
        GETEW(0);
        EW->word[0] = Pop16(emu);
        break;
    case 0x90:                      /* NOP or XCHG R8d, AX*/
    case 0x91:
    case 0x92:
    case 0x93:
    case 0x94:
    case 0x95:
    case 0x96:
    case 0x97:                      /* XCHG reg,AX */
        tmp8u = _AX+(opcode&7)+(rex.b<<3);
        if(tmp8u!=_AX) {
            if(rex.w) {
                tmp64u = R_RAX;
                R_RAX = emu->regs[tmp8u].q[0];
                emu->regs[tmp8u].q[0] = tmp64u;
            } else {
                tmp16u = R_AX;
                R_AX = emu->regs[tmp8u].word[0];
                emu->regs[tmp8u].word[0] = tmp16u;
            }
        }
        break;

    case 0x98:                               /* CBW */
        emu->regs[_AX].sword[0] = emu->regs[_AX].sbyte[0];
        break;
    case 0x99:                              /* CWD */
        R_DX=((R_AX & 0x8000)?0xFFFF:0x0000);
        break;

    case 0x9C:                              /* PUSHFW */
        CHECK_FLAGS(emu);
        Push16(emu, (uint16_t)emu->eflags.x64);
        break;
    case 0x9D:                              /* POPFW */
        CHECK_FLAGS(emu);
        emu->eflags.x64 &=0xffff0000;
        emu->eflags.x64 |= (Pop16(emu) & 0x3F7FD7) | 0x2;
        break;

    case 0xA1:                      /* MOV EAX,Od */
        if(rex.is32bits) {
            R_AX = *(uint16_t*)(uintptr_t)F32;
        } else {
            if(rex.w)
                R_RAX = *(uint64_t*)F64;
            else
                R_AX = *(uint16_t*)F64;
        }
        break;

    case 0xA3:                      /* MOV Od,EAX */
        if(rex.is32bits) {
            *(uint16_t*)(uintptr_t)F32 = R_AX;
        } else {
            if(rex.w)
                *(uint64_t*)F64 = R_RAX;
            else
                *(uint16_t*)F64 = R_AX;
        }
        break;
    case 0xA4:                      /* (REP) MOVSB */
        tmp8s = ACCESS_FLAG(F_DF)?-1:+1;
        tmp64u = (rep)?R_RCX:1L;
        while(tmp64u) {
            #ifndef TEST_INTERPRETER
            *(uint8_t*)R_RDI = *(uint8_t*)R_RSI;
            #endif
            R_RDI += tmp8s;
            R_RSI += tmp8s;
            --tmp64u;
        }
        if(rep)
            R_RCX = tmp64u;
        break;
    case 0xA5:              /* (REP) MOVSW */
        tmp8s = ACCESS_FLAG(F_DF)?-1:+1;
        tmp64u = (rep)?R_RCX:1L;
        if(rex.w) {
            tmp8s *= 8;
            while(tmp64u) {
                --tmp64u;
                *(uint64_t*)R_RDI = *(uint64_t*)R_RSI;
                R_RDI += tmp8s;
                R_RSI += tmp8s;
            }
        } else {
            tmp8s *= 2;
            while(tmp64u) {
                --tmp64u;
                *(uint16_t*)R_RDI = *(uint16_t*)R_RSI;
                R_RDI += tmp8s;
                R_RSI += tmp8s;
            }
        }
        if(rep)
            R_RCX = tmp64u;
        break;

    case 0xA7:                      /* (REPZ/REPNE) CMPSW */
        if(rex.w)
            tmp8s = ACCESS_FLAG(F_DF)?-8:+8;
        else
            tmp8s = ACCESS_FLAG(F_DF)?-2:+2;
        switch(rep) {
            case 1:
                if(R_RCX) {
                    if(rex.w) {
                        while(R_RCX) {
                            --R_RCX;
                            tmp64u3 = *(uint64_t*)R_RDI;
                            tmp64u2 = *(uint64_t*)R_RSI;
                            R_RDI += tmp8s;
                            R_RSI += tmp8s;
                            if(tmp64u3==tmp64u2)
                                break;
                        }
                        cmp64(emu, tmp64u2, tmp64u3);
                    } else {
                        while(R_RCX) {
                            --R_RCX;
                            tmp16u  = *(uint16_t*)R_RDI;
                            tmp16u2 = *(uint16_t*)R_RSI;
                            R_RDI += tmp8s;
                            R_RSI += tmp8s;
                            if(tmp16u==tmp16u2)
                                break;
                        }
                        cmp16(emu, tmp16u2, tmp16u);
                    }
                }
                break;
            case 2:
                if(R_RCX) {
                    if(rex.w) {
                        while(R_RCX) {
                            --R_RCX;
                            tmp64u3 = *(uint64_t*)R_RDI;
                            tmp64u2 = *(uint64_t*)R_RSI;
                            R_RDI += tmp8s;
                            R_RSI += tmp8s;
                            if(tmp64u3!=tmp64u2)
                                break;
                        }
                        cmp64(emu, tmp64u2, tmp64u3);
                    } else {
                        while(R_RCX) {
                            --R_RCX;
                            tmp16u  = *(uint16_t*)R_RDI;
                            tmp16u2 = *(uint16_t*)R_RSI;
                            R_RDI += tmp8s;
                            R_RSI += tmp8s;
                            if(tmp16u!=tmp16u2)
                                break;
                        }
                        cmp16(emu, tmp16u2, tmp16u);
                    }
                }
                break;
            default:
                if(rex.w) {
                    tmp64u  = *(uint64_t*)R_RDI;
                    tmp64u2 = *(uint64_t*)R_RSI;
                    R_RDI += tmp8s;
                    R_RSI += tmp8s;
                    cmp64(emu, tmp64u2, tmp64u);
                } else {
                    tmp16u  = *(uint16_t*)R_RDI;
                    tmp16u2 = *(uint16_t*)R_RSI;
                    R_RDI += tmp8s;
                    R_RSI += tmp8s;
                    cmp16(emu, tmp16u2, tmp16u);
                }
        }
        break;
    
    case 0xA9:                             /* TEST AX,Iw */
        if(rex.w)
            test64(emu, R_RAX, F32S64);
        else
            test16(emu, R_AX, F16);
        break;

    case 0xAB:                      /* (REP) STOSW */
        if(rex.w)
            tmp8s = ACCESS_FLAG(F_DF)?-8:+8;
        else
            tmp8s = ACCESS_FLAG(F_DF)?-2:+2;
        tmp64u = (rep)?R_RCX:1L;
        if((rex.w))
            while(tmp64u) {
                #ifndef TEST_INTERPRETER
                *(uint64_t*)R_RDI = R_RAX;
                #endif
                R_RDI += tmp8s;
                --tmp64u;
            }
        else
            while(tmp64u) {
                #ifndef TEST_INTERPRETER
                *(uint16_t*)R_RDI = R_AX;
                #endif
                R_RDI += tmp8s;
                --tmp64u;
            }
        if(rep)
            R_RCX = tmp64u;
        break;
    case 0xAD:                      /* (REP) LODSW */
        if(rex.w)
            tmp8s = ACCESS_FLAG(F_DF)?-8:+8;
        else
            tmp8s = ACCESS_FLAG(F_DF)?-2:+2;
        tmp64u = (rep)?R_RCX:1L;
        if((rex.w))
            while(tmp64u) {
                R_RAX = *(uint64_t*)R_RSI;
                R_RSI += tmp8s;
                --tmp64u;
            }
        else
            while(tmp64u) {
                R_AX = *(uint16_t*)R_RSI;
                R_RSI += tmp8s;
                --tmp64u;
            }
        if(rep)
            R_RCX = tmp64u;
        break;

    case 0xAF:                      /* (REPZ/REPNE) SCASW */
        if(rex.w)
            tmp8s = ACCESS_FLAG(F_DF)?-8:+8;
        else
            tmp8s = ACCESS_FLAG(F_DF)?-2:+2;
        switch(rep) {
            case 1:
                if(R_RCX) {
                    if(rex.w) {
                        while(R_RCX) {
                            --R_RCX;
                            tmp64u2 = *(uint64_t*)R_RDI;
                            R_RDI += tmp8s;
                            if(R_RAX==tmp64u2)
                                break;
                        }
                        cmp64(emu, R_RAX, tmp64u2);
                    } else {
                        while(R_RCX) {
                            --R_RCX;
                            tmp16u = *(uint16_t*)R_RDI;
                            R_RDI += tmp8s;
                            if(R_AX==tmp16u)
                                break;
                        }
                        cmp16(emu, R_AX, tmp16u);
                    }
                }
                break;
            case 2:
                if(R_RCX) {
                    if(rex.w) {
                        while(R_RCX) {
                            --R_RCX;
                            tmp64u2 = *(uint64_t*)R_RDI;
                            R_RDI += tmp8s;
                            if(R_RAX!=tmp64u2)
                                break;
                        }
                        cmp64(emu, R_RAX, tmp64u2);
                    } else {
                        while(R_RCX) {
                            --R_RCX;
                            tmp16u = *(uint16_t*)R_RDI;
                            R_RDI += tmp8s;
                            if(R_AX!=tmp16u)
                                break;
                        }
                        cmp16(emu, R_AX, tmp16u);
                    }
                }
                break;
            default:
                if(rex.w)
                    cmp64(emu, R_RAX, *(uint64_t*)R_RDI);
                else
                    cmp16(emu, R_AX, *(uint16_t*)R_RDI);
                R_RDI += tmp8s;
        }
        break;

    case 0xB8:                              /* MOV AX,Iw */
    case 0xB9:                              /* MOV CX,Iw */
    case 0xBA:                              /* MOV DX,Iw */
    case 0xBB:                              /* MOV BX,Iw */
    case 0xBC:                              /*    ...     */
    case 0xBD:
    case 0xBE:
    case 0xBF:
        if(rex.w)
            emu->regs[(opcode&7)+(rex.b<<3)].q[0] = F64;
        else
            emu->regs[(opcode&7)+(rex.b<<3)].word[0] = F16;
        break;

    case 0xC1:                              /* GRP2 Ew,Ib */
        nextop = F8;
        GETEW(1);
        tmp8u = F8 /*& 0x1f*/;
        switch((nextop>>3)&7) {
            case 0: EW->word[0] = rol16(emu, EW->word[0], tmp8u); break;
            case 1: EW->word[0] = ror16(emu, EW->word[0], tmp8u); break;
            case 2: EW->word[0] = rcl16(emu, EW->word[0], tmp8u); break;
            case 3: EW->word[0] = rcr16(emu, EW->word[0], tmp8u); break;
            case 4:
            case 6: EW->word[0] = shl16(emu, EW->word[0], tmp8u); break;
            case 5: EW->word[0] = shr16(emu, EW->word[0], tmp8u); break;
            case 7: EW->word[0] = sar16(emu, EW->word[0], tmp8u); break;
        }
        break;

    case 0xC7:                              /* MOV Ew,Iw */
        nextop = F8;
        GETEW(2);
        EW->word[0] = F16;
        break;
    case 0xC8:                      /* ENTER Iw,Ib */
        tmp16u = F16;
        tmp8u = (F8) & 0x1f;
        tmp64u = R_RBP;
        Push16(emu, R_BP);
        R_RBP = R_RSP;
        if (tmp8u) {
            for (tmp8u2 = 1; tmp8u2 < tmp8u; tmp8u2++) {
                tmp64u -= 2;
                Push16(emu, *((uint16_t*)tmp64u));
            }
            Push16(emu, R_BP);
        }
        R_RSP -= tmp16u;
        break;
    case 0xC9:                      /* LEAVE */
        R_RSP = R_RBP;
        R_BP = Pop16(emu);
        break;

    case 0xD1:                              /* GRP2 Ew,1  */
    case 0xD3:                              /* GRP2 Ew,CL */
        nextop = F8;
        GETEW(0);
        tmp8u=(opcode==0xD3)?R_CL:1;
        if(rex.w) {
            switch((nextop>>3)&7) {
                case 0: EW->q[0] = rol64(emu, EW->q[0], tmp8u); break;
                case 1: EW->q[0] = ror64(emu, EW->q[0], tmp8u); break;
                case 2: EW->q[0] = rcl64(emu, EW->q[0], tmp8u); break;
                case 3: EW->q[0] = rcr64(emu, EW->q[0], tmp8u); break;
                case 4: 
                case 6: EW->q[0] = shl64(emu, EW->q[0], tmp8u); break;
                case 5: EW->q[0] = shr64(emu, EW->q[0], tmp8u); break;
                case 7: EW->q[0] = sar64(emu, EW->q[0], tmp8u); break;
            }
        } else {
            switch((nextop>>3)&7) {
                case 0: EW->word[0] = rol16(emu, EW->word[0], tmp8u); break;
                case 1: EW->word[0] = ror16(emu, EW->word[0], tmp8u); break;
                case 2: EW->word[0] = rcl16(emu, EW->word[0], tmp8u); break;
                case 3: EW->word[0] = rcr16(emu, EW->word[0], tmp8u); break;
                case 4: 
                case 6: EW->word[0] = shl16(emu, EW->word[0], tmp8u); break;
                case 5: EW->word[0] = shr16(emu, EW->word[0], tmp8u); break;
                case 7: EW->word[0] = sar16(emu, EW->word[0], tmp8u); break;
            }
        }
        break;

    case 0xD9:                              /* x87 opcdes */
        #ifdef TEST_INTERPRETER
        return Test66D9(test, rex, addr);
        #else
        return Run66D9(emu, rex, addr);
        #endif

    case 0xDD:                              /* x87 opcdes */
        #ifdef TEST_INTERPRETER
        return Test66DD(test, rex, addr);
        #else
        return Run66DD(emu, rex, addr);
        #endif

    case 0xE8:                              /* CALL Id */
        tmp32s = F32S; // call is relative
        if(rex.is32bits)
            Push32(emu, addr);
        else
            Push64(emu, addr);
        addr += tmp32s;
        break;

    case 0xF0:                              /* LOCK: */
        #ifdef TEST_INTERPRETER
        return Test66F0(test, rex, addr);
        #else
        return Run66F0(emu, rex, addr);
        #endif

    case 0xF7:                      /* GRP3 Ew(,Iw) */
        nextop = F8;
        tmp8u = (nextop>>3)&7;
        if(rex.w) {
            GETED((tmp8u<2)?4:0);
            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]);
                    break;
            }
        } else {
            switch(tmp8u) {
                case 0: 
                case 1:                 /* TEST Ew,Iw */
                    GETEW(2);
                    test16(emu, EW->word[0], F16);
                    break;
                case 2:                 /* NOT Ew */
                    GETEW(0);
                    EW->word[0] = not16(emu, EW->word[0]);
                    break;
                case 3:                 /* NEG Ew */
                    GETEW(0);
                    EW->word[0] = neg16(emu, EW->word[0]);
                    break;
                case 4:                 /* MUL AX,Ew */
                    GETEW(0);
                    mul16(emu, EW->word[0]);
                    break;
                case 5:                 /* IMUL AX,Ew */
                    GETEW(0);
                    imul16_eax(emu, EW->word[0]);
                    break;
                case 6:                 /* DIV Ew */
                    GETEW(0);
                    div16(emu, EW->word[0]);
                    break;
                case 7:                 /* IDIV Ew */
                    GETEW(0);
                    idiv16(emu, EW->word[0]);
                    break;
            }
        }
        break;
    case 0xF8:                      /* CLC */
        CHECK_FLAGS(emu);
        CLEAR_FLAG(F_CF);
        break;
    case 0xF9:                      /* STC */
        CHECK_FLAGS(emu);
        SET_FLAG(F_CF);
        break;

    case 0xFF:                      /* GRP 5 Ew */
        nextop = F8;
        GETEW(0);
        switch((nextop>>3)&7) {
            case 0:                 /* INC Ed */
                EW->word[0] = inc16(emu, EW->word[0]);
                break;
            case 1:                 /* DEC Ed */
                EW->word[0] = dec16(emu, EW->word[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 6:                  /* Push Ew */
                Push16(emu, EW->word[0]);
                break;
            default:
                    printf_log(LOG_NONE, "Illegal Opcode %p: 66 %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;
}
