/*++

Copyright (c) 1996-1998 Microsoft Corporation

Module Name:

    miscfns.c

Abstract:
    
    Miscellaneous instuctions

Author:

    29-Jun-1995 BarryBo

Revision History:

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include "wx86.h"
#include "cpuassrt.h"
#include "threadst.h"
#include "instr.h"
#include "decoderp.h"

ASSERTNAME;

// table used by ProcessPrefixes
pfnDispatchInstruction *DispatchTables[4] = { Dispatch32, Dispatch16, LockDispatch32, LockDispatch16 };


// ---------------- single-byte functions -------------------------------
DISPATCH(ProcessPrefixes)
{
    int DataPrefix = 0;
    int LockPrefix = 0;
    int cbInstr = 0;

    for (;;) {
	switch (*(PBYTE)(eipTemp)) {
        case 0x64:          // fs:
            Instr->FsOverride = TRUE;
	    break;

	case 0xf3:	    // repz
            State->RepPrefix = PREFIX_REPZ;
	    break;

	case 0xf2:	    // repnz
            State->RepPrefix = PREFIX_REPNZ;
	    break;

	case 0xf0:	    // lock
            LockPrefix = 2;  // The lock tables are in locations 2 and 3 DispatchTables
	    break;

	case 0x2e:	    // cs:
	case 0x36:	    // ss:
	case 0x3e:	    // ds:
	case 0x26:	    // es:
        case 0x65:          // gs:
            Instr->FsOverride = FALSE;
	    break;

	case 0x66:	    // data
	    DataPrefix = 1;
	    break;

        case 0x67:          // adr
            State->AdrPrefix=TRUE;
	    break;

	default:
	    // No more prefixes found.	Set up dispatch tables based on
	    // the prefixes seen.

	    // Decode and execute the actual instruction
            ((DispatchTables[DataPrefix+LockPrefix])[GET_BYTE(eipTemp)])(State, Instr);

            // Adjust the Intel instruction size by the number of prefixes
            Instr->Size += cbInstr;

#if DBG
            // Ensure that if we saw an ADR: prefix, the decoder had code
            // to handle the prefix.  In the checked build, all functions which
            // handle the ADR: prefix clear State->AdrPrefix.
            if (State->AdrPrefix) {
                LOGPRINT((TRACELOG, "CPU Decoder: An unsupported instruction had an ADR: prefix.\r\n"
                        "Instruction Address = 0x%x.  Ignoring ADR: - this address may be data\r\n",
                        Instr->IntelAddress
                       ));
            }
#endif
            State->AdrPrefix = FALSE;

            // return to the decoder
	    return;
	}
        eipTemp++;
        cbInstr++;
    }
}
DISPATCH(bad)
{
    BAD_INSTR;
}
DISPATCH(privileged)
{
    PRIVILEGED_INSTR;
}
DISPATCH(push_es)
{
    Instr->Operation = OP_PushEs;
}
DISPATCH(pop_es)
{
    Instr->Operation = OP_PopEs;
}
DISPATCH(push_cs)
{
    Instr->Operation = OP_PushCs;
}
DISPATCH(aas)
{
    Instr->Operation = OP_Aas;
}
DISPATCH(push_ss)
{
    Instr->Operation = OP_PushSs;
}
DISPATCH(pop_ss)
{
    Instr->Operation = OP_PopSs;
}
DISPATCH(push_ds)
{
    Instr->Operation = OP_PushDs;
}
DISPATCH(pop_ds)
{
    Instr->Operation = OP_PopDs;
}
DISPATCH(daa)
{
    Instr->Operation = OP_Daa;
}
DISPATCH(das)
{
    Instr->Operation = OP_Das;
}
DISPATCH(aaa)
{
    Instr->Operation = OP_Aaa;
}
DISPATCH(aad_ib)
{
    Instr->Operation = OP_Aad;
    Instr->Operand1.Type = OPND_IMM;
    Instr->Operand1.Immed = GET_BYTE(eipTemp+1);
    Instr->Size = 2;
}
DISPATCH(imul_rw_m_iw16) // reg16 = rm16 * immediate word
{
    int cbInstr = mod_rm_reg16(State, &Instr->Operand2, &Instr->Operand1);

    Instr->Operation = OP_Imul3Arg16;
    DEREF16(Instr->Operand2);
    Instr->Operand3.Type = OPND_IMM;
    Instr->Operand3.Immed = GET_SHORT(eipTemp+1+cbInstr);
    Instr->Size = 3+cbInstr;
}
DISPATCH(imul_rw_m_iw32)
{
    int cbInstr = mod_rm_reg32(State, &Instr->Operand2, &Instr->Operand1);

    Instr->Operation = OP_Imul3Arg32;
    DEREF32(Instr->Operand2);
    Instr->Operand3.Type = OPND_IMM;
    Instr->Operand3.Immed = GET_LONG(eipTemp+1+cbInstr);
    Instr->Size = 5+cbInstr;
}
DISPATCH(imul_rw_m_ib16) // reg16 = rm16 * sign-extended immediate byte
{
    int cbInstr = mod_rm_reg16(State, &Instr->Operand2, &Instr->Operand1);

    Instr->Operation = OP_Imul3Arg16;
    DEREF16(Instr->Operand2);
    Instr->Operand3.Type = OPND_IMM;
    Instr->Operand3.Immed = (DWORD)(short)(char)GET_BYTE(eipTemp+1+cbInstr);
    Instr->Size = 2+cbInstr;
}
DISPATCH(imul_rw_m_ib32)	// reg32 = rm32 * sign-extended immediate byte
{
    int cbInstr = mod_rm_reg32(State, &Instr->Operand2, &Instr->Operand1);

    Instr->Operation = OP_Imul3Arg32;
    DEREF32(Instr->Operand2);
    Instr->Operand3.Type = OPND_IMM;
    Instr->Operand3.Immed = (DWORD)(long)(char)GET_BYTE(eipTemp+1+cbInstr);
    Instr->Size = 2+cbInstr;
}
DISPATCH(mov_mw_seg)
{
    int cbInstr = mod_rm_reg16(State, &Instr->Operand1, NULL);
    get_segreg(State, &Instr->Operand2);

    Instr->Operation = OP_Mov16;
    Instr->Size = cbInstr+1;
}
DISPATCH(mov_seg_mw)
{
    int cbInstr = mod_rm_reg16(State, &Instr->Operand2, NULL);
    get_segreg(State, &Instr->Operand1);

    Instr->Operation = OP_Mov16;
    DEREF16(Instr->Operand2);
    CPUASSERT(Instr->Operand1.Type == OPND_REGVALUE);
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Size = cbInstr+1;
}
DISPATCH(nop)
{
    Instr->Operation = OP_Nop;
}
DISPATCH(call_md)
{
    Instr->Operation = OP_CTRL_UNCOND_Callf;
    Instr->Operand1.Type = OPND_IMM;
    Instr->Operand1.Immed = eipTemp+1;
    Instr->Operand2.Type = OPND_IMM;
    Instr->Operand2.Immed = eipTemp+sizeof(ULONG)+sizeof(USHORT)+1;
    Instr->Size = sizeof(ULONG)+sizeof(USHORT)+1;
}
DISPATCH(sahf)
{
    Instr->Operation = OP_Sahf;
}
DISPATCH(lahf)
{
    Instr->Operation = OP_Lahf;
}
DISPATCH(mov_ah_ib)
{
    Instr->Operation = OP_Mov8;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_AH;
    Instr->Operand2.Type = OPND_IMM;
    Instr->Operand2.Immed = GET_BYTE(eipTemp+1);
    Instr->Size = 2;
}
DISPATCH(mov_ch_ib)
{
    Instr->Operation = OP_Mov8;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_CH;
    Instr->Operand2.Type = OPND_IMM;
    Instr->Operand2.Immed = GET_BYTE(eipTemp+1);
    Instr->Size = 2;
}
DISPATCH(mov_dh_ib)
{
    Instr->Operation = OP_Mov8;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_DH;
    Instr->Operand2.Type = OPND_IMM;
    Instr->Operand2.Immed = GET_BYTE(eipTemp+1);
    Instr->Size = 2;
}
DISPATCH(mov_bh_ib)
{
    Instr->Operation = OP_Mov8;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_BH;
    Instr->Operand2.Type = OPND_IMM;
    Instr->Operand2.Immed = GET_BYTE(eipTemp+1);
    Instr->Size = 2;
}
DISPATCH(int3)
{
    Instr->Operation = OP_Int;
}
DISPATCH(int_ib)
{
    Instr->Operation = OP_Int;
    Instr->Size = 2;
}
DISPATCH(into)
{
    Instr->Operation = OP_IntO;
}
DISPATCH(iret)
{
    Instr->Operation = OP_CTRL_INDIR_IRet;
}
DISPATCH(aam_ib)
{
    Instr->Operation = OP_Aam;
    Instr->Operand1.Type = OPND_IMM;
    Instr->Operand1.Immed = GET_BYTE(eipTemp+1);
    Instr->Size = 2;
}
DISPATCH(xlat)
{
    Instr->Operation = OP_Xlat;
}
DISPATCH(jmpf_md)
{
    Instr->Operation = OP_CTRL_UNCOND_Jmpf;
    Instr->Operand1.Type = OPND_IMM;
    Instr->Operand1.Immed = eipTemp+1;
    Instr->Size = 7;
}
DISPATCH(jmp_jb)
{
    Instr->Operand1.Type = OPND_NOCODEGEN;
    if (State->AdrPrefix) {
        Instr->Operand1.Immed = MAKELONG((short)(char)GET_BYTE(eipTemp+1)+2+(short)LOWORD(eipTemp), HIWORD(eipTemp));
#if DBG
        State->AdrPrefix = FALSE;
#endif
    } else {
        Instr->Operand1.Immed = (DWORD)(long)(char)GET_BYTE(eipTemp+1)+2+eipTemp;
    }
    if (Instr->Operand1.Immed > eipTemp) {
        Instr->Operation = OP_CTRL_UNCOND_JmpFwd;
    } else {
        Instr->Operation = OP_CTRL_UNCOND_Jmp;
    }
    Instr->Size = 2;
}
DISPATCH(cmc)
{
    Instr->Operation = OP_Cmc;
}
DISPATCH(clc)
{
    Instr->Operation = OP_Clc;
}
DISPATCH(stc)
{
    Instr->Operation = OP_Stc;
}
DISPATCH(cld)
{
    Instr->Operation = OP_Cld;
}
DISPATCH(std)
{
    Instr->Operation = OP_Std;
}
DISPATCH(GROUP_4)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);
    BYTE g = GET_BYTE(eipTemp+1);

    switch ((g >> 3) & 0x07) {
    case 0: // inc modrmB
        Instr->Operation = OP_Inc8;
        Instr->Size = 1+cbInstr;
	break;
    case 1: // dec modrmB
        Instr->Operation = OP_Dec8;
        Instr->Size = 1+cbInstr;
        break;
    default:
	BAD_INSTR;
    }
}



//-------- double-byte functions -----------------------------------------------



DISPATCH(GROUP_6)
{
    BYTE g = GET_BYTE(eipTemp+1);
    int cbInstr;

    switch ((g >> 3) & 0x07) {
    case 0: // sldt modrmw
    case 1: // str modrmw
    case 2: // lldt modrmw
    case 3: // ltr modrmw
        PRIVILEGED_INSTR;
        break;

    case 4: // verr modrmw
        cbInstr = mod_rm_reg16(State, &Instr->Operand1, NULL);
        Instr->Operation = OP_Verr;
        Instr->Size = 2+cbInstr;
        break;

    case 5: // verw modrmw
        cbInstr = mod_rm_reg16(State, &Instr->Operand1, NULL);
        Instr->Operation = OP_Verw;
        Instr->Size = 2+cbInstr;
        break;

    default:
	BAD_INSTR;	// bad
    }
}
DISPATCH(GROUP_7)
{
    BYTE g = GET_BYTE(eipTemp+1);
    int cbInstr;

    switch ((g >> 3) & 0x07) {
    case 0: // sgdt modrmw
    case 1: // sidt modrmw
    case 2: // lgdt modrmw
    case 3: // lidt modrmw
    case 6: // lmsw modrmw
        PRIVILEGED_INSTR;
        break;

    case 4: // smsw modrmw
        cbInstr = mod_rm_reg16(State, &Instr->Operand1, NULL);
        Instr->Operation = OP_Smsw;
        Instr->Size = 2+cbInstr;
        break;

    case 5: // bad
    case 7: // bad
	BAD_INSTR;
    }
}
DISPATCH(seto_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Seto;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setno_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setno;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setb_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setb;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setae_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setae;
    Instr->Size = 2+cbInstr;
}
DISPATCH(sete_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Sete;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setne_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setne;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setbe_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setbe;
    Instr->Size = 2+cbInstr;
}
DISPATCH(seta_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Seta;
    Instr->Size = 2+cbInstr;
}
DISPATCH(sets_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Sets;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setns_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setns;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setp_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setp;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setnp_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setnp;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setl_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setl;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setge_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setge;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setle_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setle;
    Instr->Size = 2+cbInstr;
}
DISPATCH(setg_modrmb)
{
    int cbInstr = mod_rm_reg8(State, &Instr->Operand1, NULL);

    Instr->Operation = OP_Setg;
    Instr->Size = 2+cbInstr;
}
DISPATCH(push_fs)
{
    Instr->Operation = OP_PushFs;
    Instr->Size = 2;
}
DISPATCH(pop_fs)
{
    Instr->Operation = OP_PopFs;
    Instr->Size = 2;
}
DISPATCH(push_gs)
{
    Instr->Operation = OP_PushGs;
    Instr->Size = 2;
}
DISPATCH(pop_gs)
{
    Instr->Operation = OP_PopGs;
    Instr->Size = 2;
}
DISPATCH(imul_regw_modrmw16) // reg16 = reg16 * mod/rm
{
    int cbInstr = mod_rm_reg16(State, &Instr->Operand2, &Instr->Operand1);

    Instr->Operation = OP_Imul16;
    DEREF32(Instr->Operand2);
    Instr->Size = 2+cbInstr;

}
DISPATCH(imul_regw_modrmw32) // reg32 = reg32 * mod/rm
{
    int cbInstr = mod_rm_reg32(State, &Instr->Operand2, &Instr->Operand1);

    Instr->Operation = OP_Imul32;
    DEREF32(Instr->Operand2);
    Instr->Size = 2+cbInstr;
}
DISPATCH(movzx_regw_modrmw)
{
    int cbInstr = mod_rm_reg16(State, &Instr->Operand1, NULL);

    DEREF16(Instr->Operand1);
    Instr->Operation = OP_Movzx16To32;
    Instr->Operand2.Type = OPND_NOCODEGEN;
    Instr->Operand2.Reg = get_reg32(State);
    Instr->Size = 2+cbInstr;
}
DISPATCH(movsx_regw_modrmw)
{
    int cbInstr = mod_rm_reg16(State, &Instr->Operand1, NULL);
    
    DEREF16(Instr->Operand1);
    Instr->Operation = OP_Movsx16To32;
    Instr->Operand2.Type = OPND_NOCODEGEN;
    Instr->Operand2.Reg = get_reg32(State);
    Instr->Size = 2+cbInstr;
}
DISPATCH(wait)
{
    Instr->Operation = OP_Wait;
}
DISPATCH(bswap_eax)
{
    Instr->Operation = OP_Bswap32;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_EAX;
    Instr->Size = 2;
}
DISPATCH(bswap_ebx)
{
    Instr->Operation = OP_Bswap32;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_EBX;
    Instr->Size = 2;
}
DISPATCH(bswap_ecx)
{
    Instr->Operation = OP_Bswap32;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_ECX;
    Instr->Size = 2;
}
DISPATCH(bswap_edx)
{
    Instr->Operation = OP_Bswap32;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_EDX;
    Instr->Size = 2;
}
DISPATCH(bswap_esp)
{
    Instr->Operation = OP_Bswap32;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_ESP;
    Instr->Size = 2;
}
DISPATCH(bswap_ebp)
{
    Instr->Operation = OP_Bswap32;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_EBP;
    Instr->Size = 2;
}
DISPATCH(bswap_esi)
{
    Instr->Operation = OP_Bswap32;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_ESI;
    Instr->Size = 2;
}
DISPATCH(bswap_edi)
{
    Instr->Operation = OP_Bswap32;
    Instr->Operand1.Type = OPND_REGREF;
    Instr->Operand1.Reg = GP_EDI;
    Instr->Size = 2;
}
DISPATCH(arpl)
{
    int cbInstr = mod_rm_reg16(State, &Instr->Operand1, &Instr->Operand2);

    Instr->Operation = OP_Arpl;
    CPUASSERT(Instr->Operand2.Type == OPND_REGREF);
    Instr->Operand2.Type = OPND_REGVALUE;
    Instr->Size = 1+cbInstr;
}
DISPATCH(cpuid)
{
    Instr->Operation = OP_CPUID;
    Instr->Size = 2;
}
DISPATCH(rdtsc)
{
    Instr->Operation = OP_Rdtsc;
    Instr->Size = 2;
}
DISPATCH(cmpxchg8b)
{
    int cbInstr = mod_rm_reg32(State, &Instr->Operand1, &Instr->Operand2);

    Instr->Operation = OP_CMPXCHG8B;
    Instr->Size = 2+cbInstr;
}
DISPATCH(LOCKcmpxchg8b)
{
    int cbInstr = mod_rm_reg32(State, &Instr->Operand1, &Instr->Operand2);

    Instr->Operation = OP_SynchLockCMPXCHG8B;
    Instr->Size = 2+cbInstr;
}