/**************************************************************************** * * 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 #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[] = {""}; /* 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 ); }