mirror of
https://github.com/NishiOwO/JWasm.git
synced 2025-04-21 16:54:39 +00:00
962 lines
39 KiB
C
962 lines
39 KiB
C
/****************************************************************************
|
|
*
|
|
* 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 );
|
|
}
|
|
|