#include        "defs.h"

unsigned long decodeLen(__in void *buffer){
        xed_decoded_inst_t     xed_decoded_inst;
        const xed_inst_t       *xed_inst;
        xed_state_t            xed_state;
        
        xed_state.mmode=XED_MACHINE_MODE_LEGACY_32;
        xed_state.stack_addr_width=XED_ADDRESS_WIDTH_32b;

        xed_decoded_inst_zero_set_mode(&xed_decoded_inst, &xed_state);
        xed_decode(&xed_decoded_inst, buffer, 16);        
        
        return xed_decoded_inst_get_length(&xed_decoded_inst);
}

unsigned long decodeIclass(__in void *buffer){
        xed_decoded_inst_t     xed_decoded_inst;
        const xed_inst_t       *xed_inst;
        xed_state_t            xed_state;
        
        xed_state.mmode=XED_MACHINE_MODE_LEGACY_32;
        xed_state.stack_addr_width=XED_ADDRESS_WIDTH_32b;

        xed_decoded_inst_zero_set_mode(&xed_decoded_inst, &xed_state);
        xed_decode(&xed_decoded_inst, buffer, 16);        
        
        return xed_decoded_inst_get_iclass(&xed_decoded_inst);
}

unsigned long decodeName(__in void *buffer, __out char *szInstDump, __in unsigned long len){
        xed_decoded_inst_t     xed_decoded_inst;
        const xed_inst_t       *xed_inst;
        xed_state_t            xed_state;
        
        xed_state.mmode=XED_MACHINE_MODE_LEGACY_32;
        xed_state.stack_addr_width=XED_ADDRESS_WIDTH_32b;

        xed_decoded_inst_zero_set_mode(&xed_decoded_inst, &xed_state);
        xed_decode(&xed_decoded_inst, buffer, 16);  
        
        memset(szInstDump, 0, len);
        xed_decoded_inst_dump_intel_format(&xed_decoded_inst, szInstDump, len, 0);
        return 0;
}

unsigned long decodeIsEipRedirection(__in px86dis px86){
        switch (px86->iclass)
        {
        case XED_ICLASS_IRET 	        :
        case XED_ICLASS_IRETD 	        :
        case XED_ICLASS_IRETQ 	        :
        case XED_ICLASS_JB 	        :
        case XED_ICLASS_JBE 	        :
        case XED_ICLASS_JL 	        :
        case XED_ICLASS_JLE 	        :
        case XED_ICLASS_JMP 	        :
        case XED_ICLASS_JMP_FAR 	:
        case XED_ICLASS_JNB 	        :
        case XED_ICLASS_JNBE 	        :
        case XED_ICLASS_JNL 	        :
        case XED_ICLASS_JNLE 	        :
        case XED_ICLASS_JNO 	        :
        case XED_ICLASS_JNP 	        :
        case XED_ICLASS_JNS 	        :
        case XED_ICLASS_JNZ 	        :
        case XED_ICLASS_JO 	        :
        case XED_ICLASS_JP 	        :
        case XED_ICLASS_JRCXZ 	        :
        case XED_ICLASS_JS 	        :
        case XED_ICLASS_JZ              :
        case XED_ICLASS_LOOP 	        :
        case XED_ICLASS_LOOPE 	        :
        case XED_ICLASS_LOOPNE          :
        case XED_ICLASS_CALL_FAR 	:
        case XED_ICLASS_CALL_NEAR       :
        case XED_ICLASS_RET_FAR 	:
        case XED_ICLASS_RET_NEAR        :
                return 1;
        case XED_ICLASS_SYSENTER        :
                //__debugbreak();
                return 1;
        case XED_ICLASS_UD2:    
                return 1;
        case XED_ICLASS_INVALID:
                return 1;
        case XED_ICLASS_INT             :
        case XED_ICLASS_INT1            :
        case XED_ICLASS_INT3            :
                return 1;               
        case XED_ICLASS_PUSHFD          :
        case XED_ICLASS_POPFD           :
                return 1;
//        case XED_ICLASS_RDTSC           :
//                return 1;

        }
        
        return 0;        
}

unsigned long decodeIsJcc(__in px86dis px86){
        switch (px86->iclass)
        {
        case XED_ICLASS_JB 	        :
        case XED_ICLASS_JBE 	        :
        case XED_ICLASS_JL 	        :
        case XED_ICLASS_JLE 	        :
        case XED_ICLASS_JNB 	        :
        case XED_ICLASS_JNBE 	        :
        case XED_ICLASS_JNL 	        :
        case XED_ICLASS_JNLE 	        :
        case XED_ICLASS_JNO 	        :
        case XED_ICLASS_JNP 	        :
        case XED_ICLASS_JNS 	        :
        case XED_ICLASS_JNZ 	        :
        case XED_ICLASS_JO 	        :
        case XED_ICLASS_JP 	        :
        case XED_ICLASS_JS 	        :
        case XED_ICLASS_JZ              :
                return 1;
        }
        return 0;        
}

unsigned long decodeIsInt(__in px86dis px86){
        switch (px86->iclass)
        {
        case XED_ICLASS_INT             :
        case XED_ICLASS_INT1            :
        case XED_ICLASS_INT3            :
                return 1;
        }
        
        return 0;        
}

unsigned long decodeInstruction(__in void *buffer, px86dis px86)
{
        xed_decoded_inst_t     xed_decoded_inst;
        const xed_inst_t       *xed_inst;
        xed_state_t            xed_state;
        const xed_operand_t    *xed_operand;
        xed_operand_enum_t     op_name;
        xed_operand_visibility_enum_t  xed_visibility;
        xed_reg_enum_t         reg;
        const xed_operand_values_t* ov;   
        unsigned long   index, oindex;     
        unsigned long   *poperand_flags, *poperand, *poperand_size;
        unsigned long   mem_index;
        unsigned long   disp_bits;
        unsigned char   *ptr;
        
        unsigned char    mod;
        unsigned char    rm;
        unsigned char    regopcode;
        char             inst_dump[128];
        
        xed_state.mmode=XED_MACHINE_MODE_LEGACY_32;
        xed_state.stack_addr_width=XED_ADDRESS_WIDTH_32b;
        
        memset(px86, 0, sizeof(x86dis));
        px86->iclass = XED_ICLASS_INVALID;
        
        xed_decoded_inst_zero_set_mode(&xed_decoded_inst, &xed_state);
        if (XED_ERROR_NONE != xed_decode(&xed_decoded_inst, buffer, 16)){
                px86->iclass = XED_ICLASS_INVALID;
                return 0;
        }
        xed_inst                     = xed_decoded_inst_inst(&xed_decoded_inst);
        
        /******************************************************
         * get number of operands
         ******************************************************/
        px86->num_of_operands      = xed_inst_noperands(xed_inst);
        ov = xed_decoded_inst_operands(&xed_decoded_inst);
        px86->iclass               = xed_decoded_inst_get_iclass(&xed_decoded_inst);
        px86->len                  = xed_decoded_inst_get_length(&xed_decoded_inst);                
        //dones't return proper data back... returns 0 when there is sib
        //if (xed_operand_values_has_sib_byte(ov))
        //        op_index++;
        
        
        //begin decoding instruction!!!
        poperand_flags = px86->operand_flags;
        poperand       = px86->operand;
        poperand_size  = px86->operand_size;

        oindex    = 0;
        mem_index = 0;

        for (index = 0; index < px86->num_of_operands; index++){
                xed_operand = xed_inst_operand(xed_inst, index);        
                op_name     = xed_operand_name(xed_operand);

                //detect if this instruction writtes to memory in any possible way...
                if (op_name == XED_OPERAND_AGEN || op_name == XED_OPERAND_MEM0 || op_name == XED_OPERAND_MEM1){
                        if (xed_decoded_inst_mem_written(&xed_decoded_inst, mem_index)){
                                px86->mem_write = 1;
                        }                   
                        
                }
                
                xed_visibility = xed_operand_operand_visibility(xed_operand);
                if (xed_visibility == XED_OPVIS_INVALID || xed_visibility == XED_OPVIS_SUPPRESSED || xed_visibility == XED_OPVIS_LAST){
                        if (op_name == XED_OPERAND_AGEN || op_name == XED_OPERAND_MEM0 || XED_OPERAND_MEM1)
                                mem_index++;
                        continue;
                }
                
                
                switch (op_name)
                {
                case XED_OPERAND_AGEN:
                case XED_OPERAND_MEM0:
                case XED_OPERAND_MEM1:
                        poperand_flags[oindex] = C_MEM;
                        
                        //check if there is seg prefix other then DS
                        //check if sib is used
                        //check base reg used
                        //check scale used
                        //check displacement
                        
                        reg  = xed_decoded_inst_get_seg_reg(&xed_decoded_inst, mem_index);
                        if (reg == XED_REG_FS){
                               poperand_flags[oindex] |= C_SEG_USED;
                               px86->seg       = reg;
                               //DbgPrint(("%s -- found segment register... %.08X", __FUNCTION__, buffer ));
                               //DbgPrint(("%s -- operand index            %.08X", __FUNCTION__, oindex));
                        }

                        reg  = xed_decoded_inst_get_base_reg(&xed_decoded_inst, mem_index);
                        if (reg != XED_REG_INVALID){
                                poperand_flags[oindex] |= C_BASE_USED;
                                px86->base      = reg;
                        }
                        reg  = xed_decoded_inst_get_index_reg(&xed_decoded_inst, mem_index);
                        if (reg != XED_REG_INVALID){
                                poperand_flags[oindex] |= C_INDEX_USED;
                                px86->index     = reg;
                        }
                        reg  = xed_decoded_inst_get_scale(&xed_decoded_inst, mem_index);
                        if (reg != XED_REG_INVALID){
                                poperand_flags[oindex] |= C_SCALE_USED;
                                px86->scale     = reg;
                        }
                        disp_bits = xed_decoded_inst_get_memory_displacement_width(&xed_decoded_inst, mem_index);
                        if (disp_bits){
                               reg  = xed_decoded_inst_get_memory_displacement(&xed_decoded_inst, mem_index);
                               poperand_flags[oindex]  |= C_DISPLACEMENT_USED;
                               px86->displacement = reg;
                        }

                        poperand_size[oindex] = xed_decoded_inst_get_memory_operand_length(&xed_decoded_inst, mem_index);
                        
                        if (xed_decoded_inst_mem_read(&xed_decoded_inst, mem_index)){
                                px86->mem_read_write = MEM_READ;
                        }
                        if (xed_decoded_inst_mem_written(&xed_decoded_inst, mem_index)){
                                px86->mem_read_write = MEM_WRITE;
                        }        
                        mem_index++;                                                
                        break;
                case XED_OPERAND_PTR:
                        //DbgPrint(("XED_OPERAND_PTR......"));
                        poperand_flags[oindex] |= C_IMM;
                        poperand[oindex]       = xed_decoded_inst_get_branch_displacement(&xed_decoded_inst);
                        poperand_size[oindex]  = xed_decoded_inst_get_branch_displacement_width(&xed_decoded_inst);
                        break;
                case XED_OPERAND_RELBR:
                        //jnz/jc/jmp... blah blah...
                        poperand_flags[oindex] |= C_IMM;
                        poperand[oindex]       = xed_decoded_inst_get_branch_displacement(&xed_decoded_inst);
                        poperand_size[oindex]  = xed_decoded_inst_get_branch_displacement_width(&xed_decoded_inst);
                        break;

                case XED_OPERAND_IMM0:
                        poperand_flags[oindex] |= C_IMM;
                        if (xed_decoded_inst_get_immediate_is_signed(&xed_decoded_inst))
                                poperand[oindex] = xed_decoded_inst_get_signed_immediate(&xed_decoded_inst);
                        else
                                poperand[oindex] = (ULONG)xed_decoded_inst_get_unsigned_immediate(&xed_decoded_inst);
                        break;
                case XED_OPERAND_IMM1:
                        poperand_flags[oindex] |= C_IMM;
                        if (xed_decoded_inst_get_immediate_is_signed(&xed_decoded_inst))
                                poperand[oindex] = xed_decoded_inst_get_signed_immediate(&xed_decoded_inst);
                        else
                                poperand[oindex] = (ULONG)xed_decoded_inst_get_unsigned_immediate(&xed_decoded_inst);
                        break;
                        DbgPrint(("XED_OPERAND_IMM1......"));
                        break;

                case XED_OPERAND_REG0:
                case XED_OPERAND_REG1:
                case XED_OPERAND_REG2:
                case XED_OPERAND_REG3:
                case XED_OPERAND_REG4:
                case XED_OPERAND_REG5:
                case XED_OPERAND_REG6:
                case XED_OPERAND_REG7:
                case XED_OPERAND_REG8:
                case XED_OPERAND_REG9:
                case XED_OPERAND_REG10:
                case XED_OPERAND_REG11:
                case XED_OPERAND_REG12:
                case XED_OPERAND_REG13:
                case XED_OPERAND_REG14:
                case XED_OPERAND_REG15:
                case XED_OPERAND_BASE0:
                case XED_OPERAND_BASE1:
                        reg = xed_decoded_inst_get_reg(&xed_decoded_inst, op_name);
                        poperand[oindex] = reg;
                        poperand_flags[oindex] |= C_REG;

                        //get operand size
                        poperand_size[oindex] = xed_decoded_inst_operand_length_bits(&xed_decoded_inst, index) / 8;
                        break;
                default:
                        DbgPrint(("XED_OPERAND_UNKNOWN..."));
                        __debugbreak();
                        break;
                }
                
                oindex++;

        }

        px86->num_of_operands = oindex;
        
        return 0;
}