asmmatch.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 887 行 · 第 1/2 页

C
887
字号
/****************************************************************************
*
*                            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:  WHEN YOU FIGURE OUT WHAT THIS FILE DOES, PLEASE
*               DESCRIBE IT HERE!
*
****************************************************************************/


#include "asmglob.h"

#include "asmins.h"
#include "asmdefs.h"
#include "asmalloc.h"
#include "asmfixup.h"

#if defined( _STANDALONE_ )

#include "directiv.h"

extern void AddLinnumDataRef( void );
extern int  AddFloatingPointEmulationFixup( const struct asm_ins ASMFAR *, bool );

#endif

static int match_phase_3( int *i, OPNDTYPE determinant );

static int output_3DNow( int i )
/******************************/
{
    const struct asm_ins ASMFAR *ins = &AsmOpTable[i];

    if( ins->byte1_info == F_0F0F ) {
        AsmCodeByte( ins->opcode | Code->info.opcode );
    }
    return( NOT_ERROR );
}

static int output( int i )
/************************/
/*
- determine what code should be output and their order;
- output prefix bytes ( ADRSIZ, OPSIZ, LOCK, REPxx, segment override prefix, etc )
  , opcode, "mod r/m" byte and "s-i-b" byte;
*/
{
    const struct asm_ins ASMFAR *ins = &AsmOpTable[i];
    struct asm_code             *rCode = Code;
    unsigned_8                  tmp;

#if defined( _STANDALONE_ )
    /*
     * Output debug info - line numbers
     */
    if( Options.debug_flag && !PhaseError && (Parse_Pass != PASS_1) ) {
        AddLinnumDataRef();
    }

    /*
     * Check if FP is valid
     */
    if((( ins->cpu & P_FPU_MASK ) != P_NO87 ) && ( Options.floating_point == NO_FP_ALLOWED )) {
        AsmError( NO_FP_WITH_FPC_SET );
        return( ERROR );
    }
    /*
     * Output FP fixup if required
     */
    if(( Options.floating_point == DO_FP_EMULATION )
        && ( !rCode->use32 )
        && ( ins->allowed_prefix != NO_FWAIT )
        && (( ins->allowed_prefix == FWAIT ) || (( ins->cpu&P_FPU_MASK ) != P_NO87 ))) {
            if( AddFloatingPointEmulationFixup( ins, FALSE ) == ERROR ) {
                return( ERROR );
            }
    }
#endif

    /*
     * Check if CPU and FPU is valid for output code
     */
    if( ( ins->cpu & P_CPU_MASK ) > ( rCode->info.cpu & P_CPU_MASK )
        || ( ins->cpu & P_FPU_MASK ) > ( rCode->info.cpu & P_FPU_MASK )
        || ( ins->cpu & P_EXT_MASK ) > ( ins->cpu & rCode->info.cpu & P_EXT_MASK ) ) {
        AsmError( INVALID_INSTRUCTION_WITH_CURRENT_CPU_SETTING );
        return( ERROR );
    }

    /*
     * Output instruction prefix LOCK, REP or REPNE
     */
    if( rCode->prefix.ins != EMPTY ) {
        switch( rCode->prefix.ins ) {
        case T_LOCK:
            if( ins->allowed_prefix != LOCK ) {
                AsmError( LOCK_PREFIX_IS_NOT_ALLOWED_ON_THIS_INSTRUCTION );
                return( ERROR );
            }
            break;
        case T_REP:
            if( ins->allowed_prefix != REP ) {
                AsmError( REP_PREFIX_IS_NOT_ALLOWED_ON_THIS_INSTRUCTION );
                return( ERROR );
            }
            break;
        case T_REPZ:
        case T_REPE:
        case T_REPNZ:
        case T_REPNE:
            if( ins->allowed_prefix != REPxx ) {
                AsmError( REPX_PREFIX_IS_NOT_ALLOWED_ON_THIS_INSTRUCTION );
                return( ERROR );
            }
            break;
        default:
            break;
        }
        AsmCodeByte( AsmOpTable[AsmOpcode[rCode->prefix.ins].position].opcode );
    }
    /*
     * Output instruction prefix REP or REPNE for SSEx instructions
     */
    switch( ins->byte1_info ) {
    case F_F20F:
        AsmCodeByte( 0xF2 );
        break;
    case F_F3:
    case F_F30F:
        AsmCodeByte( 0xF3 );
        break;
    case F_16:
    case F_32:
    case F_660F:
    case F_0F:
    case F_0F0F:
    default:
        break;
    }

    /*
     * Output FP FWAIT if required
     */
    if( ins->token == T_FWAIT ) {
        if(( rCode->info.cpu&P_CPU_MASK ) < P_386 ) {
#if defined( _STANDALONE_ )
            if(( Options.floating_point == DO_FP_EMULATION ) && ( !rCode->use32 )) {
                AsmCodeByte( OP_NOP );
            }
#else
            AsmCodeByte( OP_NOP );
#endif
        }
    } else if( ins->allowed_prefix == FWAIT ) {
        AsmCodeByte( OP_WAIT );
#if defined( _STANDALONE_ )
    } else if(( Options.floating_point == DO_FP_EMULATION )
        && ( !rCode->use32 )
        && ( ins->allowed_prefix != NO_FWAIT )
        && (( ins->allowed_prefix == FWAIT ) || (( ins->cpu&P_FPU_MASK ) != P_NO87 ))) {
        AsmCodeByte( OP_WAIT );
#endif
    } else if( ins->allowed_prefix != NO_FWAIT ) {
        // implicit FWAIT synchronization for 8087 (CPU 8086/80186)
        if((( rCode->info.cpu&P_CPU_MASK ) < P_286 )
            && (( ins->cpu&P_FPU_MASK ) == P_87 )) {
            AsmCodeByte( OP_WAIT );
        }
    }

#if defined( _STANDALONE_ )
    /*
     * Output FP fixup if required
     */
    if(( Options.floating_point == DO_FP_EMULATION )
        && ( !rCode->use32 )
        && ( ins->allowed_prefix != NO_FWAIT )
        && (( ins->allowed_prefix == FWAIT ) || (( ins->cpu&P_FPU_MASK ) != P_NO87 ))) {
        if( AddFloatingPointEmulationFixup( ins, TRUE ) == ERROR ) {
            return( ERROR );
        }
    }
#endif
    /*
     * Output address size prefix
     */
    if( rCode->prefix.adrsiz == TRUE ) {
        AsmCodeByte( ADRSIZ );
    }
    /*
     * Output operand size prefix
     */
    if( ins->cpu & NO_OPPRFX )
        rCode->prefix.opsiz = FALSE;
    switch( ins->byte1_info ) {
    case F_16:
        if( rCode->use32 ) rCode->prefix.opsiz = TRUE;
        break;
    case F_32:
        if( !rCode->use32 ) rCode->prefix.opsiz = TRUE;
        break;
    case F_660F:
        rCode->prefix.opsiz = TRUE;
        break;
    case F_0F:
    case F_0F0F:
    case F_F20F:
    case F_F30F:
    default:
        break;
    }
    if( rCode->prefix.opsiz == TRUE ) {
        /*
            Certain instructions use the ADDRSIZE prefix when they really
            should use OPERSIZE prefix (well, I think so!). Stupid Intel.
        */
        switch( ins->token ) {
        case T_JCXZ:
        case T_JECXZ:
        case T_LOOPD:
        case T_LOOPW:
        case T_LOOPED:
        case T_LOOPEW:
        case T_LOOPNED:
        case T_LOOPNEW:
        case T_LOOPZD:
        case T_LOOPZW:
        case T_LOOPNZD:
        case T_LOOPNZW:
            AsmCodeByte( ADRSIZ );
            break;
        default:
            AsmCodeByte( OPSIZ );
            break;
        }
    }
    /*
     * Output segment prefix
     */
    if( rCode->prefix.seg != EMPTY ) {
        AsmCodeByte( rCode->prefix.seg );
    }
    /*
     * Output extended opcode
     * special case for some 286 and 386 instructions
     * or 3DNow!, MMX and SSEx instructions
     */
    switch( ins->byte1_info ) {
    case F_0F0F:
        AsmCodeByte( EXTENDED_OPCODE );
        // no break
    case F_0F:
    case F_660F:
    case F_F20F:
    case F_F30F:
        AsmCodeByte( EXTENDED_OPCODE );
        break;
    case F_F3:
    default:
        break;
    }
    if( ins->opnd_dir ) {
        /* The reg and r/m fields are backwards */
        tmp = rCode->info.rm_byte;
        rCode->info.rm_byte = ( tmp & 0xc0 ) | ((tmp >> 3) & 0x7) | ((tmp << 3) & 0x38);
    }
    switch( ins->rm_info ) {
    case R_in_OP:
        AsmCodeByte( ins->opcode | ( rCode->info.rm_byte & NOT_BIT_67 ) );
        break;
    case no_RM:
        AsmCodeByte( ins->opcode | rCode->info.opcode );
        break;
    case no_WDS:
        rCode->info.opcode = 0;
        // no break
    default:
        // don't output opcode for 3DNow! instructions
        if( ins->byte1_info != F_0F0F ) {
            AsmCodeByte( ins->opcode | rCode->info.opcode );
        }
        tmp = ins->rm_byte | rCode->info.rm_byte;
        AsmCodeByte( tmp );
        if( addr_32( rCode ) ) {
            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
                 AsmCodeByte( rCode->sib );
            }
        }
        break;
    }
    return( NOT_ERROR );
}

static int output_data( OPNDTYPE determinant, int index )
/*******************************************************/
/*
  output address displacement and immediate data;
*/
{
    int                 out = 0;

    mark_fixupp( determinant, index );
    switch( Code->info.token ) {
    case T_CMPS:
    case T_LODS:
    case T_MOVS:
    case T_OUTS:
    case T_INS:
    case T_SCAS:
    case T_STOS:
    case T_XLAT:
        /* these instructions don't really want the memory operand */
        return( NOT_ERROR );
    }

#if defined( _STANDALONE_ )
    store_fixup( index );
#endif

    if( determinant & OP_I8 ) {   // 8 bit
        out = 1;
    } else if( determinant & OP_I16 ) { // 16 bit
        out = 2;
    } else if( determinant & ( OP_I32 | OP_J32 ) ) { // 32 bit
        out = 4;
    } else if( determinant & OP_J48 ) {
        out = 6;
    } else if( determinant & OP_M_ANY ) {
        // switch on the mode ( the leftmost 2 bits )
        switch( Code->info.rm_byte & BIT_67 ) {
        case MOD_01:  // mode = 01
            out = 1;
            break;
        case MOD_00:
            if( !addr_32( Code ) ) {
                if( ( Code->info.rm_byte & BIT_012 ) == D16 ) {
                     out = 2;
                }
            } else {
                switch( Code->info.rm_byte & BIT_012 ) {
                case S_I_B:
                    if( ( Code->sib & BIT_012 ) != D32 ) {
                        break;  // out = 0
                    }
                    // no break
                case D32:
                    out = 4;
                }
            }
            break;
        case MOD_10:  // mode = 10
            if( !addr_32( Code ) ) {
                out = 2;
            } else {
                out = 4;
            }
        }
    }
    while( out > 0 ) {
        AsmCodeByte( Code->data[index] );
        Code->data[index] >>= 8;
        out--;
    }
    return( NOT_ERROR );
}

static int match_phase_2( int *i )
/*********************************
- a routine used by match_phase_1() to determine whether both operands match
  with that in the assembly instructions table;
- call by match_phase_1() only;
*/
{
    if( Code->info.opnd_type[OPND2] != OP_NONE ) {
        // 2 opnds instruction
        return( match_phase_3( i, AsmOpTable[*i].opnd_type[OPND1] ) );
    } else {
        // 1 opnd instruction
        // make sure the second opnd also match, i.e. has to be OP_NONE
        if( AsmOpTable[*i].opnd_type[OPND2] == OP_NONE ) {
            if( output( *i ) == ERROR ) {
                return( ERROR );
            }
            // output idata or disp ( if first opnd is OP_M / OP_I )
            return( output_data( Code->info.opnd_type[OPND1], OPND1 ) );
        } else {
            // still cannot find match
            return( EMPTY );
        }
    }
}

int match_phase_1( void )
/************************
- this routine will look up the assembler opcode table and try to match
  the first operand in table with what we get;
- if first operand match then it will call match_phase_2() to determine if the
  second operand also match; if not, it must be error;
*/
{
    int             i;
    int             retcode;
    signed char     temp_opsiz = 0;
    OPNDTYPE        cur_opnd;
    OPNDTYPE        asm_op1;

    // if nothing inside, no need to output anything
    if( Code->info.token == T_NULL ) {
        if( Code->prefix.seg != EMPTY ) {
            /* we have:     REG: on line */
            AsmError( SYNTAX_ERROR );
            return( ERROR );
        } else if( Code->info.opnd_type[OPND1] == OP_NONE ) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?