/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  instruction encoding, scans opcode table and emits code.
*
****************************************************************************/

#include <limits.h>

#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "codegen.h"
#include "fixup.h"
#include "fpfixup.h"
#include "segment.h"
#include "input.h"
#include "listing.h"
#include "reswords.h"

extern const struct opnd_class opnd_clstab[];
#if AVXSUPP
extern struct ReservedWord  ResWordTable[];
extern const uint_8               vex_flags[];
#endif

const char szNull[] = {"<NULL>"};

/* v2.03: OutputCodeByte no longer needed */
#define OutputCodeByte( x ) OutputByte( x )

/* segment order must match the one in special.h */
enum prefix_reg {
    PREFIX_ES = 0x26,
    PREFIX_CS = 0x2E,
    PREFIX_SS = 0x36,
    PREFIX_DS = 0x3E,
    PREFIX_FS = 0x64,
    PREFIX_GS = 0x65
};

static const char sr_prefix[] =
    { PREFIX_ES, PREFIX_CS, PREFIX_SS, PREFIX_DS, PREFIX_FS, PREFIX_GS };

static void output_opc( struct code_info *CodeInfo )
/**************************************************/
/*
 * - determine what code should be output and their order.
 * - output prefix bytes:
 *    - LOCK, REPxx,
 *    - FWAIT (not a prefix, but handled like one)
 *    - address size prefix 0x67
 *    - operand size prefix 0x66
 *    - segment override prefix, branch hints
 * - output opcode (1-3), "mod r/m" and "s-i-b" bytes.
 *
 * Note that jwasm follows Masm strictly here, even if it
 * contradicts Intel docs. For example, Masm always emits
 * the F2/F3/66 byte before a segment prefix, even if the
 * F2/F3/66 byte is a "mantadory prefix".
 */
{
    const struct instr_item *ins = CodeInfo->pinstr;
    uint_8           tmp;
    uint_8           fpfix = FALSE;

    DebugMsg1(("output_opc enter, ins.opc/rm=%X/%X, byte1_info=%X CodeInfo->rm=%X opsiz=%u\n", ins->opcode, ins->rm_byte, ins->byte1_info, CodeInfo->rm_byte, CodeInfo->prefix.opsiz ));
    /*
     * Output debug info - line numbers
     */
    if( Options.line_numbers )
        AddLinnumDataRef( get_curr_srcfile(), GetLineNumber() );

    /* if it's a FPU instr, reset opsiz */
    //if( ins->cpu & P_FPU_MASK ) {
    /* v2.02: if it's a FPU or MMX/SSE instr, reset opsiz!
     * [this code has been moved here from codegen()]
     */
    if( ins->cpu & ( P_FPU_MASK | P_MMX | P_SSEALL ) ) {
#if SSE4SUPP
        /* there are 2 exceptions. how to avoid this ugly hack? */
        if ( CodeInfo->token != T_CRC32 &&
            CodeInfo->token != T_POPCNT )
#endif
        CodeInfo->prefix.opsiz = FALSE;
    }

    /*
     * Check if CPU, FPU and extensions are within the limits
     */
    if( ( ins->cpu & P_CPU_MASK ) > ( ModuleInfo.curr_cpu & P_CPU_MASK )
        || ( ins->cpu & P_FPU_MASK ) > ( ModuleInfo.curr_cpu & P_FPU_MASK )
        || ( ins->cpu & P_EXT_MASK ) > ( ModuleInfo.curr_cpu & P_EXT_MASK ) ) {
        DebugMsg(("output_opc: wrong cpu setting: instr.cpu=%X, ModuleInfo.cpu=%X\n",
                  ins->cpu, ModuleInfo.curr_cpu ));
        /* if instruction is valid for 16bit cpu, but operands aren't,
         then display a more specific error message! */
        if( ins->cpu == P_386 &&
            ( ( InstrTable[ IndexFromToken( CodeInfo->token )].cpu & P_CPU_MASK ) <= P_386 ))
            EmitError( INSTRUCTION_FORM_REQUIRES_80386 );
        else
            EmitError( INSTRUCTION_OR_REGISTER_NOT_ACCEPTED_IN_CURRENT_CPU_MODE );
        //return( ERROR );
    }

    /*
     * Output FP fixup if required
     * the OPs with NOWAIT are the instructions beginning with
     * FN, except FNOP.
     * the OPs with WAIT are the instructions:
     * FCLEX, FDISI, FENI, FINIT, FSAVEx, FSTCW, FSTENVx, FSTSW
     */
    if(( ModuleInfo.emulator == TRUE ) &&
       ( CodeInfo->Ofssize == USE16 ) &&
       ( ins->cpu & P_FPU_MASK ) &&
       ( ins->allowed_prefix != AP_NO_FWAIT ) ) {
        fpfix = TRUE;
        /* v2.04: no error is returned */
        AddFloatingPointEmulationFixup( CodeInfo );
    }

    /*
     * Output instruction prefix LOCK, REP or REP[N]E|Z
     */
    if( CodeInfo->prefix.ins != EMPTY ) {
        tmp = InstrTable[ IndexFromToken( CodeInfo->prefix.ins )].allowed_prefix;
        /* instruction prefix must be ok. However, with -Zm, the plain REP
         * is also ok for instructions which expect REPxx.
         */
        if ( ModuleInfo.m510 == TRUE &&
            tmp == AP_REP &&
            ins->allowed_prefix == AP_REPxx )
            tmp = AP_REPxx;

        if( ins->allowed_prefix != tmp ) {
            EmitError( INSTRUCTION_PREFIX_NOT_ALLOWED );
        } else
            OutputCodeByte( InstrTable[ IndexFromToken( CodeInfo->prefix.ins )].opcode );
    }
    /*
     * Output FP FWAIT if required
     */
    if ( ins->cpu & P_FPU_MASK ) {
        if( CodeInfo->token == T_FWAIT ) {
            /* v2.04: Masm will always insert a NOP if emulation is active,
             * no matter what the current cpu is. The reason is simple: the
             * nop is needed because of the fixup which was inserted.
             */
            //if(( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_386 ) {
            //    if(( ModuleInfo.emulator == TRUE ) && ( CodeInfo->Ofssize == USE16 )) {
            if( fpfix ) {
                OutputCodeByte( OP_NOP );
            }
        } else if( fpfix || ins->allowed_prefix == AP_FWAIT ) {
            OutputCodeByte( OP_WAIT );
        } else if( ins->allowed_prefix != AP_NO_FWAIT ) {
            /* implicit FWAIT synchronization for 8087 (CPU 8086/80186) */
            if(( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_286 )
               OutputCodeByte( OP_WAIT );
        }
    }

    /*
     * check if address/operand size prefix is to be set
     */
    switch( ins->byte1_info ) {
    case F_16:
        if( CodeInfo->Ofssize >= USE32 ) CodeInfo->prefix.opsiz = TRUE;
        break;
    case F_32:
        if( CodeInfo->Ofssize == USE16 ) CodeInfo->prefix.opsiz = TRUE;
        break;
    case F_16A: /* 16-bit JCXZ and LOOPcc */
        /* doesnt exist for IA32+ */
        if( CodeInfo->Ofssize == USE32 ) CodeInfo->prefix.adrsiz = TRUE;
        break;
    case F_32A: /* 32-bit JECXZ and LOOPcc */
#if AMD64_SUPPORT
        /* in IA32+, the 32bit version gets an 0x67 prefix */
        if ( CodeInfo->Ofssize != USE32)  CodeInfo->prefix.adrsiz = TRUE;
#else
        if( CodeInfo->Ofssize == USE16 ) CodeInfo->prefix.adrsiz = TRUE;
#endif
        break;
    case F_0FNO66:
        CodeInfo->prefix.opsiz = FALSE;
        break;
#if AMD64_SUPPORT
    case F_48:
    case F_480F:
        CodeInfo->prefix.rex |= REX_W;
        break;
#endif
    }

#if AVXSUPP
    if ( !( ResWordTable[CodeInfo->token].flags & RWF_VEX ) ) {
#endif
        switch ( ins->byte1_info ) {
        case F_660F:
        case F_660F38:
        case F_660F3A:
            CodeInfo->prefix.opsiz = TRUE;
            break;
        case F_F20F:
        case F_F20F38: OutputCodeByte( 0xF2 ); break;
        case F_F3: /* PAUSE instruction */
        case F_F30F:   OutputCodeByte( 0xF3 ); break;
        }
#if AVXSUPP
    }
#endif
    /*
     * Output address and operand size prefixes.
     * These bytes are NOT compatible with FP emulation fixups,
     * which expect that the FWAIT/NOP first "prefix" byte is followed
     * by either a segment prefix or the opcode byte.
     * Neither Masm nor JWasm emit a warning, though.
     */
    if( CodeInfo->prefix.adrsiz == TRUE ) {
        OutputCodeByte( ADRSIZ );
#ifdef DEBUG_OUT
        if ( fpfix )
            DebugMsg(("output_opc: ERROR: FP emulation byte sequence destroyed by 32-bit address prefix!\n"));
#endif
    }
    if( CodeInfo->prefix.opsiz == TRUE ) {
#if 1
        if(( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_386 ) {
            DebugMsg(("output_opc: instruction form requires 386\n"));
            EmitError( INSTRUCTION_FORM_REQUIRES_80386 );
            //return( ERROR ); /* v2.06: don't skip instruction */
        }
#endif
        OutputCodeByte( OPSIZ );
    }
    /*
     * Output segment prefix
     */
    if( CodeInfo->prefix.RegOverride != EMPTY ) {
        OutputCodeByte( sr_prefix[CodeInfo->prefix.RegOverride] );
    }

    if( ins->opnd_dir ) {
        /* The reg and r/m fields are backwards */
        tmp = CodeInfo->rm_byte;
        CodeInfo->rm_byte = ( tmp & 0xc0 ) | ((tmp >> 3) & 0x7) | ((tmp << 3) & 0x38);
#if AMD64_SUPPORT
        tmp = CodeInfo->prefix.rex;
        CodeInfo->prefix.rex = ( tmp & 0xFA ) | (( tmp & REX_R ) >> 2 ) | (( tmp & REX_B ) << 2 );
#endif
    }

#if AVXSUPP
    if ( ResWordTable[CodeInfo->token].flags & RWF_VEX ) {
        uint_8 lbyte = 0;
        switch ( ins->byte1_info ) {
        case F_660F:
        case F_660F38:
        case F_660F3A:
            lbyte |= 0x01;
            break;
        case F_F30F:
            lbyte |= 0x02;
            break;
        case F_F20F:
        case F_F20F38:
            lbyte |= 0x03;
            break;
        }

        if ( ( CodeInfo->opnd[OPND1].type & OP_YMM ) ||
            ( CodeInfo->opnd[OPND2].type & ( OP_YMM | OP_M256 ) ) ||
            ( CodeInfo->opnd[OPND1].type == OP_NONE && /* no operands? use VX_L flag from vex_flags[] */
             vex_flags[ CodeInfo->token - VEX_START ] & VX_L ) )
            lbyte |= 0x04;

        if ( CodeInfo->vexregop )
            lbyte |= ( ( 16 - CodeInfo->vexregop ) << 3 );
        else
            lbyte |= 0x78;

        /* emit 2 (0xC4) or 3 (0xC5) byte VEX prefix */
        if ( ins->byte1_info >= F_0F38 || ( CodeInfo->prefix.rex & ( REX_B | REX_X | REX_W ) ) ) {
            uint_8 byte1 = 0;
            OutputCodeByte( 0xC4 );
            switch ( ins->byte1_info ) {
            case F_0F38:
            case F_660F38:
            case F_F20F38:
                byte1 |= 0x02;
                break;
            case F_0F3A:
            case F_660F3A:
                byte1 |= 0x03;
                break;
            default:
                if ( ins->byte1_info >= F_0F )
                    byte1 |= 0x01;
            }
            byte1 |= (( CodeInfo->prefix.rex & REX_B ) ? 0 : 0x20 );
            byte1 |= (( CodeInfo->prefix.rex & REX_X ) ? 0 : 0x40 );
            byte1 |= (( CodeInfo->prefix.rex & REX_R ) ? 0 : 0x80 );
            OutputCodeByte( byte1 );
            lbyte |= ( ( CodeInfo->prefix.rex & REX_W ) ? 0x80 : 0 );
            OutputCodeByte( lbyte );
        } else {
            lbyte |= ( ( CodeInfo->prefix.rex & REX_R ) ? 0 : 0x80 );
            OutputCodeByte( 0xC5 );
            OutputCodeByte( lbyte );
        }
    } else {
#endif

#if AMD64_SUPPORT
    /* the REX prefix must be located after the other prefixes */
    if( CodeInfo->prefix.rex != 0 ) {
        if ( CodeInfo->Ofssize != USE64 ) {
            EmitError( INVALID_OPERAND_SIZE );
        }
        OutputCodeByte( CodeInfo->prefix.rex | 0x40 );
    }
#endif

    /*
     * Output extended opcode
     * special case for some 286 and 386 instructions
     * or 3DNow!, MMX and SSEx instructions
     */
    if ( ins->byte1_info >= F_0F ) {
        OutputCodeByte( EXTENDED_OPCODE );
        switch ( ins->byte1_info ) {
        case F_0F0F:   OutputCodeByte( EXTENDED_OPCODE ); break;
        case F_0F38:
        case F_F20F38:
        case F_660F38: OutputCodeByte( 0x38 );            break;
        case F_0F3A:
        case F_660F3A: OutputCodeByte( 0x3A );            break;
        }
    }

#if AVXSUPP
    }
#endif

    switch( ins->rm_info ) {
    case R_in_OP:
        OutputCodeByte( ins->opcode | ( CodeInfo->rm_byte & NOT_BIT_67 ) );
        break;
    case no_RM:
        OutputCodeByte( ins->opcode | CodeInfo->iswide );
        break;
    case no_WDS:
        CodeInfo->iswide = 0;
        /* no break */
    default: /* opcode (with w d s bits), rm-byte */
        /* don't emit opcode for 3DNow! instructions */
        if( ins->byte1_info != F_0F0F ) {
            OutputCodeByte( ins->opcode | CodeInfo->iswide | CodeInfo->opc_or );
        }
        /* emit ModRM byte; bits 7-6 = Mod, bits 5-3 = Reg, bits 2-0 = R/M */
        tmp = ins->rm_byte | CodeInfo->rm_byte;
        OutputCodeByte( tmp );

        if( ( CodeInfo->Ofssize == USE16 && CodeInfo->prefix.adrsiz == 0 ) ||
           ( CodeInfo->Ofssize == USE32 && CodeInfo->prefix.adrsiz == 1 ) )
            return; /* no SIB for 16bit */

        switch ( tmp & NOT_BIT_345 ) {
        case 0x04: /* mod = 00, r/m = 100, s-i-b is present */
        case 0x44: /* mod = 01, r/m = 100, s-i-b is present */
        case 0x84: /* mod = 10, r/m = 100, s-i-b is present */
            /* emit SIB byte; bits 7-6 = Scale, bits 5-3 = Index, bits 2-0 = Base */
            OutputCodeByte( CodeInfo->sib );
        }
    }

    return;
}

static void output_data( const struct code_info *CodeInfo, enum operand_type determinant, int index )
/***************************************************************************************************/
/*
 * output address displacement and immediate data;
 */
{
    int       size = 0;

    /* skip the memory operand for XLAT/XLATB and string instructions! */
    if ( CodeInfo->token == T_XLAT || CodeInfo->token == T_XLATB ||
        CodeInfo->pinstr->allowed_prefix == AP_REP ||
        CodeInfo->pinstr->allowed_prefix == AP_REPxx ) {
        /* v2.06: no need anymore to modify the fixup field, it's
         * used inside OutputBytes() only.
         */
        //CodeInfo->InsFixup[index] = NULL;
        return;
    }
#ifdef DEBUG_OUT
    if ( CodeInfo->opnd[index].InsFixup )
        DebugMsg1(("output_data(idx=%u, op=%" I32_SPEC "X [data=%" I32_SPEC "X fixup=%p typ=%u] ) enter [rm=%X]\n", index, determinant, CodeInfo->opnd[index].data32l, CodeInfo->opnd[index].InsFixup, CodeInfo->opnd[index].InsFixup->type ,CodeInfo->rm_byte ));
    else
        DebugMsg1(("output_data(idx=%u, op=%" I32_SPEC "X [data=%" I32_SPEC "X fixup=NULL] ) enter [rm=%X]\n", index, determinant, CodeInfo->opnd[index].data32l, CodeInfo->rm_byte ));
#endif

    /* determine size */

    if( determinant & OP_I8 ) {
        size = 1;
    } else if( determinant & OP_I16 ) {
        size = 2;
    } else if( determinant & OP_I32 ) {
        size = 4;
    } else if( determinant & OP_I48 ) {
        size = 6;
#if AMD64_SUPPORT
    } else if( determinant & OP_I64 ) {
        size = 8;
#endif
    } else if( determinant & OP_M_ANY ) {
        /* switch on the mode ( the leftmost 2 bits ) */
        switch( CodeInfo->rm_byte & BIT_67 ) {
        case MOD_01:  /* 8-bit displacement */
            size = 1;
            break;
        case MOD_00: /* direct; base and/or index with no disp */
            if( ( CodeInfo->Ofssize == USE16 && CodeInfo->prefix.adrsiz == 0 ) ||
               ( CodeInfo->Ofssize == USE32 && CodeInfo->prefix.adrsiz == 1 ) ) {
                if( ( CodeInfo->rm_byte & BIT_012 ) == RM_D16 ) {
                     size = 2; /* = size of displacement */
                }
            } else {
#if AMD64_SUPPORT
                /* v2.11: special case, 64-bit direct memory addressing, opcodes 0xA0 - 0xA3 */
                if( CodeInfo->Ofssize == USE64 && ( CodeInfo->pinstr->opcode & 0xFC ) == 0xA0 && CodeInfo->pinstr->byte1_info == 0 )
                    size = 8;
                else
#endif
                switch( CodeInfo->rm_byte & BIT_012 ) {
                case RM_SIB: /* 0x04 (equals register # for ESP) */
                    if( ( CodeInfo->sib & BIT_012 ) != RM_D32 ) {
                        break;  /* size = 0 */
                    }
                    /* no break */
                case RM_D32: /* 0x05 (equals register # for EBP) */
                    size = 4; /* = size of displacement */
#if AMD64_SUPPORT
                    /* v2.11: overflow check for 64-bit added */
                    if ( CodeInfo->Ofssize == USE64 && CodeInfo->opnd[index].data64 >= 0x80000000 && CodeInfo->opnd[index].data64 < 0xffffffff80000000 )
                        EmitErr( INVALID_INSTRUCTION_OPERANDS );
#endif
                }
            }
            break;
        case MOD_10:  /* 16- or 32-bit displacement */
            if( ( CodeInfo->Ofssize == USE16 && CodeInfo->prefix.adrsiz == 0 ) ||
               ( CodeInfo->Ofssize == USE32 && CodeInfo->prefix.adrsiz == 1 ) ) {
                size = 2;
            } else {
                size = 4;
            }
        }
    }
#ifdef DEBUG_OUT
    if ( size > 4 )
        DebugMsg1(( "output_data: size=%u cont=%" I64_SPEC "X\n", size, CodeInfo->opnd[index].data64 ));
    else if ( size )
        DebugMsg1(( "output_data: size=%u cont=%" I32_SPEC "X\n", size, CodeInfo->opnd[index].data32l ));
    else
        DebugMsg1(( "output_data: size=0\n" ));
#endif
    if ( size ) {
        if ( CodeInfo->opnd[index].InsFixup ) {
            /* v2.07: fixup type check moved here */
            if ( Parse_Pass > PASS_1 )
                if ( ( 1 << CodeInfo->opnd[index].InsFixup->type ) & ModuleInfo.fmtopt->invalid_fixup_type ) {
                    EmitErr( UNSUPPORTED_FIXUP_TYPE,
                           ModuleInfo.fmtopt->formatname,
                           CodeInfo->opnd[index].InsFixup->sym ? CodeInfo->opnd[index].InsFixup->sym->name : szNull );
                    /* don't exit! */
                }
            if ( write_to_file ) {
                CodeInfo->opnd[index].InsFixup->locofs = GetCurrOffset();
                OutputBytes( (unsigned char *)&CodeInfo->opnd[index].data32l,
                            size, CodeInfo->opnd[index].InsFixup );
                return;
            }
        }
        OutputBytes( (unsigned char *)&CodeInfo->opnd[index].data32l, size, NULL );
    }
    return;
}

static ret_code check_3rd_operand( struct code_info *CodeInfo )
/*************************************************************/
{
    if( ( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd == OP3_NONE ) ||
       ( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd == OP3_HID ) )
        return( ( CodeInfo->opnd[OPND3].type == OP_NONE ) ? NOT_ERROR : ERROR );

    /* current variant needs a 3rd operand */
    DebugMsg1(("check_3rd_operand: tab=%X <-> codeinfo=%X\n", opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd, CodeInfo->opnd[OPND3].type ));

    switch ( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd ) {
    case OP3_CL:
        if ( CodeInfo->opnd[OPND3].type == OP_CL )
            return( NOT_ERROR );
        break;
    case OP3_I8_U: /* IMUL, SHxD, a few MMX/SSE */
        /* for IMUL, the operand is signed! */
        if ( ( CodeInfo->opnd[OPND3].type & OP_I ) && CodeInfo->opnd[OPND3].data32l >= -128 ) {
            if ( ( CodeInfo->token == T_IMUL && CodeInfo->opnd[OPND3].data32l < 128 ) ||
                ( CodeInfo->token != T_IMUL && CodeInfo->opnd[OPND3].data32l < 256 ) ) {
                CodeInfo->opnd[OPND3].type = OP_I8;
                return( NOT_ERROR );
            }
        }
        break;
    case OP3_I: /* IMUL */
        if ( CodeInfo->opnd[OPND3].type & OP_I )
            return( NOT_ERROR );
        break;
    case OP3_XMM0:
#if AVXSUPP
        /* for VEX encoding, XMM0 has the meaning: any XMM/YMM register */
        if ( CodeInfo->token >= VEX_START ) {
            if ( CodeInfo->opnd[OPND3].type & ( OP_XMM | OP_YMM ) )
                return( NOT_ERROR );
        } else
#endif
        if ( CodeInfo->opnd[OPND3].type == OP_XMM &&
            CodeInfo->opnd[OPND3].data32l == 0 )
            return( NOT_ERROR );
        break;
    }
    return( ERROR );
}

static void output_3rd_operand( struct code_info *CodeInfo )
/**********************************************************/
{
    if( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd == OP3_I8_U ) {
        DebugMsg1(("output_3rd_operand, expected I8, op3=%" I32_SPEC "X\n", CodeInfo->opnd[OPND3].type ));
        /* v2.06: the type has been checked already! */
        //if( CodeInfo->opnd_type[OPND3] & OP_I ) {
        output_data( CodeInfo, OP_I8, OPND3 );
        //} else {
        //    EmitError( INVALID_INSTRUCTION_OPERANDS );
        //    return;
        //}
    } else if( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd == OP3_I ) {
        output_data( CodeInfo, CodeInfo->opnd[OPND3].type, OPND3 );
    } else if( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd == OP3_HID ) {
        DebugMsg1(("output_3rd_operand, expected OP3_HID, op3=%" I32_SPEC "X\n", CodeInfo->opnd[OPND3].type ));
        /* v2.06: to avoid having to add 3*8 operand categories there's a
         * hard-wired peculiarity for the "hidden" 3rd operand: it's calculated
         * directly from the instruction token. in instruct.h, CMPEQPD must
         * be first and the order of the following CMP entries must not be
         * changed.
         */
        //CodeInfo->data[OPND3] = opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd & ~OP3_HID;
        CodeInfo->opnd[OPND3].data32l = ( CodeInfo->token - T_CMPEQPD ) % 8;
        CodeInfo->opnd[OPND3].InsFixup = NULL;
        output_data( CodeInfo, OP_I8, OPND3 );
    }
#if AVXSUPP
    else if( CodeInfo->token >= VEX_START &&
            opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd == OP3_XMM0 ) {
        CodeInfo->opnd[OPND3].data32l = ( CodeInfo->opnd[OPND3].data32l << 4 );
        output_data( CodeInfo, OP_I8, OPND3 );
    }
#endif
    return;
}

static ret_code match_phase_3( struct code_info *CodeInfo, enum operand_type opnd1 )
/***********************************************************************************
 * - this routine will look up the assembler opcode table and try to match
 *   the second operand with what we get;
 * - if second operand match then it will output code; if not, pass back to
 *   codegen() and continue to scan InstrTable;
 * - possible return codes: NOT_ERROR (=done), ERROR (=nothing found)
 */
{
    enum operand_type    determinant = opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[OPND1]; /* remember first op type */
    enum operand_type    opnd2 = CodeInfo->opnd[OPND2].type;
    enum operand_type    tbl_op2;

    DebugMsg1(("match_phase_3 enter, opnd1=%" I32_SPEC "X, searching op2=%" I32_SPEC "X\n", opnd1, opnd2 ));

#if AVXSUPP
    if ( CodeInfo->token >= VEX_START && ( vex_flags[ CodeInfo->token - VEX_START ] & VX_L ) ) {
        if ( CodeInfo->opnd[OPND1].type & ( OP_YMM | OP_M256) ) {
            if ( opnd2 & OP_YMM )
                opnd2 |= OP_XMM;
            else if ( opnd2 & OP_M256 )
                opnd2 |= OP_M128;
            else if ( opnd2 & OP_M128 )
                opnd2 |= OP_M64;
            else if ( ( opnd2 & OP_XMM ) && !( vex_flags[ CodeInfo->token - VEX_START ] & VX_HALF ) ) {
                EmitError( INSTRUCTION_OR_REGISTER_NOT_ACCEPTED_IN_CURRENT_CPU_MODE );
                return( ERROR );
            }
        }
#if 1
        /* may be necessary to cover the cases where the first operand is a memory operand
         * "without size" and the second operand is a ymm register
         */
        else if ( CodeInfo->opnd[OPND1].type == OP_M ) {
            if ( opnd2 & OP_YMM )
                opnd2 |= OP_XMM;
        }
#endif
    }
#endif
    do  {
        tbl_op2 = opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[OPND2];
        DebugMsg1(("match_phase_3: instr table op2=%" I32_SPEC "X\n", tbl_op2 ));
        switch( tbl_op2 ) {
        case OP_I: /* arith, MOV, IMUL, TEST */
            if( opnd2 & tbl_op2 ) {
                DebugMsg1(("match_phase_3: matched OP_I\n"));
                /* This branch exits with either ERROR or NOT_ERROR.
                 * So it can modify the CodeInfo fields without harm.
                 */
                if( opnd1 & OP_R8 ) {
                    /* 8-bit register, so output 8-bit data */
                    /* v2.04: the check has already happened in check_size() or idata_xxx() */
                    //if( Parse_Pass == PASS_1 && !InRange( operand, 1 ) ) {
                    //    DebugMsg(("imm const too large (08): %X\n", operand));
                    //    EmitWarn( 1, IMMEDIATE_CONSTANT_TOO_LARGE );
                    //}
                    CodeInfo->prefix.opsiz = FALSE;
                    opnd2 = OP_I8;
                    if( CodeInfo->opnd[OPND2].InsFixup != NULL ) {
                    /* v1.96: make sure FIX_HIBYTE isn't overwritten! */
                        if ( CodeInfo->opnd[OPND2].InsFixup->type != FIX_HIBYTE )
                            CodeInfo->opnd[OPND2].InsFixup->type = FIX_OFF8;
                    }
                } else if( opnd1 & OP_R16 ) {
                    /* v2.04: the check has already happened in check_size() or idata_xxx() */
                    //if( Parse_Pass == PASS_1 && !InRange( operand, 2 ) ) {
                    //    DebugMsg(("imm const too large (16): %X\n", operand));
                    //    EmitWarn( 1, IMMEDIATE_CONSTANT_TOO_LARGE );
                    //}
                    /* 16-bit register, so output 16-bit data */
                    opnd2 = OP_I16;
#if AMD64_SUPPORT
                } else if( opnd1 & (OP_R32 | OP_R64 ) ) {
#else
                } else if( opnd1 & OP_R32 ) {
#endif
                    /* 32- or 64-bit register, so output 32-bit data */
                    CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? 0 : 1;/* 12-feb-92 */
                    opnd2 = OP_I32;
                } else if( opnd1 & OP_M ) {
                    /* there is no reason this should be only for T_MOV */
                    switch( OperandSize( opnd1, CodeInfo ) ) {
                    case 1:
                        opnd2 = OP_I8;
                        CodeInfo->prefix.opsiz = FALSE;
                        break;
                    case 2:
                        opnd2 = OP_I16;
                        CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? 1 : 0;
                        break;
#if AMD64_SUPPORT
                        /* mov [mem], imm64 doesn't exist. It's ensured that
                         * immediate data is 32bit only
                         */
                    case 8:
#endif
                    case 4:
                        opnd2 = OP_I32;
                        CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? 0 : 1;
                        break;
                    default:
                        EmitError( INVALID_INSTRUCTION_OPERANDS );
                        //return( ERROR ); /* v2.06: don't exit */
                    }
                }
                output_opc( CodeInfo );
                output_data( CodeInfo, opnd1, OPND1 );
                output_data( CodeInfo, opnd2, OPND2 );
                return( NOT_ERROR );
            }
            break;
        case OP_I8_U: /* shift+rotate, ENTER, BTx, IN, PSxx[D|Q|W] */
            if( opnd2 & tbl_op2 ) {
                DebugMsg1(("match_phase_3: matched OP_I8_U\n"));
                if ( CodeInfo->const_size_fixed && opnd2 != OP_I8 )
                    break;
                /* v2.03: lower bound wasn't checked */
                /* range of unsigned 8-bit is -128 - +255 */
                if( CodeInfo->opnd[OPND2].data32l <= UCHAR_MAX && CodeInfo->opnd[OPND2].data32l >= SCHAR_MIN ) {
                    /* v2.06: if there's an external, adjust the fixup if it is > 8-bit */
                    if ( CodeInfo->opnd[OPND2].InsFixup != NULL ) {
                        if ( CodeInfo->opnd[OPND2].InsFixup->type == FIX_OFF16 ||
                            CodeInfo->opnd[OPND2].InsFixup->type == FIX_OFF32 )
                            CodeInfo->opnd[OPND2].InsFixup->type = FIX_OFF8;
                    }
                    /* the SSE4A EXTRQ instruction will need this! */
                    //if( check_3rd_operand( CodeInfo ) == ERROR )
                    //  break;
                    output_opc( CodeInfo );
                    output_data( CodeInfo, opnd1, OPND1 );
                    output_data( CodeInfo, OP_I8, OPND2 );
                    //if( CodeInfo->pinstr->opnd_type_3rd != OP3_NONE )
                    //output_3rd_operand( CodeInfo );
                    return( NOT_ERROR );
                }
            }
            break;
        case OP_I8: /* arith, IMUL */
            /* v2.06: this case has been rewritten */

            /* v2.04: added */
            if( ModuleInfo.NoSignExtend &&
               ( CodeInfo->token == T_AND ||
                CodeInfo->token == T_OR ||
                CodeInfo->token == T_XOR ) )
                break;

            /* v2.11: skip externals - but don't skip undefines; forward8.asm */
            //if ( CodeInfo->opnd[OPND2].InsFixup != NULL ) /* external? then skip */
            if ( CodeInfo->opnd[OPND2].InsFixup != NULL && CodeInfo->opnd[OPND2].InsFixup->sym->state != SYM_UNDEFINED ) /* external? then skip */
                break;

            if ( CodeInfo->const_size_fixed == FALSE )
                if ( ( opnd1 & ( OP_R16 | OP_M16 ) ) && (int_8)CodeInfo->opnd[OPND2].data32l == (int_16)CodeInfo->opnd[OPND2].data32l )
                    tbl_op2 |= OP_I16;
                else if ( ( opnd1 & ( OP_RGT16 | OP_MGT16 ) ) && (int_8)CodeInfo->opnd[OPND2].data32l == (int_32)CodeInfo->opnd[OPND2].data32l )
                    tbl_op2 |= OP_I32;

            if( opnd2 & tbl_op2 ) {
                DebugMsg1(("match_phase_3: matched OP_I8\n"));
                output_opc( CodeInfo );
                output_data( CodeInfo, opnd1, OPND1 );
                output_data( CodeInfo, OP_I8, OPND2 );
                return( NOT_ERROR );
            }
            break;
        case OP_I_1: /* shift ops */
            if( opnd2 & tbl_op2 ) {
               if ( CodeInfo->opnd[OPND2].data32l == 1 ) {
                   DebugMsg1(("match_phase_3: matched OP_I_1\n"));
                   output_opc( CodeInfo );
                   output_data( CodeInfo, opnd1, OPND1 );
                   /* the immediate is "implicite" */
                   return( NOT_ERROR );
               }
            }
            break;
        default:
            /* v2.06: condition made more restrictive */
            //if( ( opnd2 & tbl_op2 ) || (CodeInfo->mem_type == MT_EMPTY && (opnd2 & OP_M_ANY) && (tbl_op2 & OP_M_ANY) )) {
            if( opnd2 & tbl_op2 ) {
                if( check_3rd_operand( CodeInfo ) == ERROR )
                    break;
                DebugMsg1(("match_phase_3: matched opnd2\n" ));
                output_opc( CodeInfo );
                if ( opnd1 & (OP_I_ANY | OP_M_ANY ) )
                    output_data( CodeInfo, opnd1, OPND1 );
                if ( opnd2 & (OP_I_ANY | OP_M_ANY ) )
                    output_data( CodeInfo, opnd2, OPND2 );
                //if( CodeInfo->pinstr->opnd_type_3rd != OP3_NONE )
                if( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type_3rd != OP3_NONE )
                    output_3rd_operand( CodeInfo );
                if( CodeInfo->pinstr->byte1_info == F_0F0F ) /* output 3dNow opcode? */
                    OutputCodeByte( CodeInfo->pinstr->opcode | CodeInfo->iswide );
                return( NOT_ERROR );
            }
            break;
        }
        CodeInfo->pinstr++;
    } while ( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[OPND1] == determinant && CodeInfo->pinstr->first == FALSE );
    CodeInfo->pinstr--; /* pointer will be increased in codegen() */
    DebugMsg(("match_phase_3: returns EMPTY\n"));
    return( ERROR );
}

static ret_code check_operand_2( struct code_info *CodeInfo, enum operand_type opnd1 )
/*************************************************************************************
 * check if a second operand has been entered.
 * If yes, call match_phase_3();
 * else emit opcode and optional data.
 * possible return codes: ERROR (=nothing found), NOT_ERROR (=done)
 */
{
    if( CodeInfo->opnd[OPND2].type == OP_NONE ) {

        if( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[OPND2] != OP_NONE )
            return( ERROR ); /* doesn't match */

        /* 1 opnd instruction found */

        /* v2.06: added check for unspecified size of mem op */
        if ( opnd1 == OP_M ) {
            const struct instr_item *next = CodeInfo->pinstr+1;
            if ( ( opnd_clstab[next->opclsidx].opnd_type[OPND1] & OP_M ) &&
                next->first == FALSE )
                /* skip error if mem op is a forward reference */
                /* v2.06b: added "undefined" check */
                if ( CodeInfo->undef_sym == FALSE &&
                    ( CodeInfo->opnd[OPND1].InsFixup == NULL ||
                     CodeInfo->opnd[OPND1].InsFixup->sym == NULL ||
                     CodeInfo->opnd[OPND1].InsFixup->sym->state != SYM_UNDEFINED ) ) {
                    DebugMsg(("check_operand_2: error: undef_sym=%u Fixup[0]=%X [%s]\n",
                              CodeInfo->undef_sym, CodeInfo->opnd[OPND1].InsFixup,
                              CodeInfo->opnd[OPND1].InsFixup ? CodeInfo->opnd[OPND1].InsFixup->sym ? CodeInfo->opnd[OPND1].InsFixup->sym->name : "NULL" : "NULL" ));
                    EmitErr( INSTRUCTION_OPERAND_MUST_HAVE_SIZE );
                }
        }

        output_opc( CodeInfo );
        output_data( CodeInfo, opnd1, OPND1 );
#if AMD64_SUPPORT
        if ( CodeInfo->Ofssize == USE64 && CodeInfo->opnd[OPND1].InsFixup && CodeInfo->opnd[OPND1].InsFixup->type == FIX_RELOFF32 )
            CodeInfo->opnd[OPND1].InsFixup->addbytes = GetCurrOffset() - CodeInfo->opnd[OPND1].InsFixup->locofs;
#endif
        return( NOT_ERROR );
    }

    /* check second operand */
    if ( match_phase_3( CodeInfo, opnd1 ) == NOT_ERROR ) {
#if AMD64_SUPPORT
        /* for rip-relative fixups, the instruction end is needed */
        if ( CodeInfo->Ofssize == USE64 ) {
            if ( CodeInfo->opnd[OPND1].InsFixup && CodeInfo->opnd[OPND1].InsFixup->type == FIX_RELOFF32 )
                CodeInfo->opnd[OPND1].InsFixup->addbytes = GetCurrOffset() - CodeInfo->opnd[OPND1].InsFixup->locofs;
            if ( CodeInfo->opnd[OPND2].InsFixup && CodeInfo->opnd[OPND2].InsFixup->type == FIX_RELOFF32 )
                CodeInfo->opnd[OPND2].InsFixup->addbytes = GetCurrOffset() - CodeInfo->opnd[OPND2].InsFixup->locofs;
        }
#endif
        return( NOT_ERROR );
    }
    return( ERROR );
}

ret_code codegen( struct code_info *CodeInfo, uint_32 oldofs )
/*************************************************************
 * - codegen() will look up the assembler opcode table and try to find
 *   a matching first operand;
 * - if one is found then it will call check_operand_2() to determine
 *   if further operands also match; else, it must be error.
 */
{
    ret_code           retcode = ERROR;
    enum operand_type  opnd1;
    enum operand_type  tbl_op1;

    /* privileged instructions ok? */
    if( ( CodeInfo->pinstr->cpu & P_PM ) > ( ModuleInfo.curr_cpu & P_PM ) ) {
        EmitError( INSTRUCTION_OR_REGISTER_NOT_ACCEPTED_IN_CURRENT_CPU_MODE );
        return( ERROR );
    }
    opnd1 = CodeInfo->opnd[OPND1].type;

    /* if first operand is immediate data, set compatible flags */
    if( opnd1 & OP_I ) {
        if( opnd1 == OP_I8 ) {
            opnd1 = OP_IGE8;
        } else if( opnd1 == OP_I16 ) {
            opnd1 = OP_IGE16;
        }
    }

#if AVXSUPP
    if ( CodeInfo->token >= VEX_START && ( vex_flags[ CodeInfo->token - VEX_START ] & VX_L ) ) {
        if ( opnd1 & ( OP_YMM | OP_M256 ) ) {
            if ( CodeInfo->opnd[OPND2].type & OP_XMM && !( vex_flags[ CodeInfo->token - VEX_START ] & VX_HALF ) ) {
                EmitErr( INVALID_INSTRUCTION_OPERANDS );
                return( ERROR );
            }
            if ( opnd1 & OP_YMM )
                opnd1 |= OP_XMM;
            else
                opnd1 |= OP_M128;
        }
    }
#endif

#if AMD64_SUPPORT
    DebugMsg1(("codegen(ofs=%X): %s opnd1=%X codeinfo: ofssize=%u wide=%u rm=%Xh sib=%Xh rex=%Xh opsiz=%u\n",
               CurrSeg->sym.offset, GetResWName( CodeInfo->token, NULL ),
               opnd1, CodeInfo->Ofssize, CodeInfo->iswide,
               CodeInfo->rm_byte, CodeInfo->sib,
               CodeInfo->prefix.rex, CodeInfo->prefix.opsiz ));
#endif
    /* scan the instruction table for a matching first operand */
    do  {
        tbl_op1 = opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[OPND1];

        //DebugMsg1(("codegen: table.op1=%X\n", tbl_op1 ));

        /* v2.06: simplified */
        if ( tbl_op1 == OP_NONE && opnd1 == OP_NONE ) {
            output_opc( CodeInfo );
            if ( CurrFile[LST] )
                LstWrite( LSTTYPE_CODE, oldofs, NULL );
            return( NOT_ERROR );
        } else if ( opnd1 & tbl_op1 ) {
            /* for immediate operands, the idata type has sometimes
             * to be modified in opnd_type[OPND1], to make output_data()
             * emit the correct number of bytes. */
            switch( tbl_op1 ) {
            case OP_I32: /* CALL, JMP, PUSHD */
            case OP_I16: /* CALL, JMP, RETx, ENTER, PUSHW */
                retcode = check_operand_2( CodeInfo, tbl_op1 );
                break;
            case OP_I8_U: /* INT xx; OUT xx, AL */
                if( CodeInfo->opnd[OPND1].data32l <= UCHAR_MAX && CodeInfo->opnd[OPND1].data32l >= SCHAR_MIN ) {
                    retcode = check_operand_2( CodeInfo, OP_I8 );
                }
                break;
            case OP_I_3: /* INT 3 */
                if ( CodeInfo->opnd[OPND1].data32l == 3 ) {
                    retcode = check_operand_2( CodeInfo, OP_NONE );
                }
                break;
            default:
                retcode = check_operand_2( CodeInfo, CodeInfo->opnd[OPND1].type );
                break;
            }
            if( retcode == NOT_ERROR ) {
                if ( CurrFile[LST] )
                    LstWrite( LSTTYPE_CODE, oldofs, NULL );
                return( NOT_ERROR );
            }
        }
        CodeInfo->pinstr++;
    } while ( CodeInfo->pinstr->first == FALSE );

    DebugMsg(("codegen: no matching format found\n"));
    EmitError( INVALID_INSTRUCTION_OPERANDS );
    return( ERROR );
}

