mirror of
https://github.com/NishiOwO/JWasm.git
synced 2025-04-21 16:54:39 +00:00
633 lines
28 KiB
C
633 lines
28 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: parser's processing of branch instructions with immediate
|
|
* operand (CALL, JMP, Jxx, LOOPxx, JxCXZ). Includes:
|
|
* - "far call optimisation": a "call FAR ptr <proc>" is
|
|
* exchanged by a "push cs" + "call NEAR ptr <proc>".
|
|
* - "short jump extension": a conditional jump with a
|
|
* destination not within the SHORT range is exchanged
|
|
* by "j<cond> $+3|5" and "jmp <dest>" if cpu is < 386
|
|
* (see OPTION LJMP | NOLJMP).
|
|
****************************************************************************/
|
|
|
|
#include <limits.h>
|
|
|
|
#include "globals.h"
|
|
#include "parser.h"
|
|
#include "fixup.h"
|
|
#include "expreval.h"
|
|
#include "fastpass.h"
|
|
#include "segment.h"
|
|
#include "input.h"
|
|
#include "assume.h"
|
|
#include "codegen.h"
|
|
#include "myassert.h"
|
|
|
|
#define NEEDLABEL 1 /* 1=better Masm compatibility */
|
|
|
|
#define IS_CONDJMP( inst ) ( ( inst >= T_JA ) && ( inst <= T_JZ ) )
|
|
|
|
/* opsize byte (0x66) to be generated? */
|
|
#define OPSIZE( s, x ) ( ( s != x ) ? TRUE : FALSE )
|
|
|
|
/* v2.03: OutputCodeByte no longer needed */
|
|
#define OutputCodeByte( x ) OutputByte( x )
|
|
|
|
extern ret_code segm_override( const struct expr *opndx, struct code_info *CodeInfo );
|
|
extern struct asym *SegOverride;
|
|
|
|
/* "short jump extension": extend a (conditional) jump.
|
|
* example:
|
|
* "jz label"
|
|
* is converted to
|
|
* "jnz SHORT $+x" ( x = sizeof(next ins), may be 3|5|6|7|8 )
|
|
* "jmp label"
|
|
*
|
|
* there is a problem if it's a short forward jump with a distance
|
|
* of 7D-7F (16bit), because the additional "jmp label" will increase
|
|
* the code size.
|
|
*/
|
|
static void jumpExtend( struct code_info *CodeInfo, int far_flag )
|
|
/****************************************************************/
|
|
{
|
|
//uint_8 opcode;
|
|
unsigned next_ins_size;
|
|
|
|
if( Parse_Pass == PASS_2 )
|
|
EmitWarn( 4, EXTENDING_JUMP );
|
|
|
|
DebugMsg(("jumpExtend(far=%u), pass=%u, curr offset=%X, Ofssize=%u\n", far_flag, Parse_Pass + 1, GetCurrOffset(), CodeInfo->Ofssize ));
|
|
if( far_flag ) {
|
|
if ( CodeInfo->prefix.opsiz ) {
|
|
/* it's 66 EA OOOO SSSS or 66 EA OOOOOOOO SSSS */
|
|
next_ins_size = CodeInfo->Ofssize ? 6 : 8;
|
|
} else {
|
|
/* it's EA OOOOOOOO SSSS or EA OOOO SSSS */
|
|
next_ins_size = CodeInfo->Ofssize ? 7 : 5;
|
|
}
|
|
} else {
|
|
/* it's E9 OOOOOOOO or E9 OOOO */
|
|
next_ins_size = CodeInfo->Ofssize ? 5 : 3;
|
|
}
|
|
|
|
/* it's ensured that the short jump version is first in InstrTable */
|
|
//opcode = InstrTable[optable_idx[CodeInfo->token]].opcode;
|
|
//OutputCodeByte( opcode ^ 1 );
|
|
/* the negation is achieved by XOR 1 */
|
|
OutputCodeByte( CodeInfo->pinstr->opcode ^ 1 );
|
|
OutputCodeByte( next_ins_size );
|
|
CodeInfo->token = T_JMP;
|
|
CodeInfo->pinstr = &InstrTable[ IndexFromToken( T_JMP )];
|
|
|
|
return;
|
|
}
|
|
|
|
/* "far call optimisation": a far call is done to a near label
|
|
* optimize (call SSSS:OOOO -> PUSH CS, CALL OOOO)
|
|
*/
|
|
static void FarCallToNear( struct code_info *CodeInfo )
|
|
/*****************************************************/
|
|
{
|
|
if( Parse_Pass == PASS_2 )
|
|
EmitWarn( 4, CALL_FAR_TO_NEAR );
|
|
|
|
OutputCodeByte( 0x0E ); /* 0x0E is "PUSH CS" opcode */
|
|
CodeInfo->mem_type = MT_NEAR;
|
|
|
|
return;
|
|
}
|
|
|
|
ret_code process_branch( struct code_info *CodeInfo, unsigned CurrOpnd, const struct expr *opndx )
|
|
/************************************************************************************************/
|
|
/*
|
|
* called by idata_fixup(), idata_nofixup().
|
|
* current instruction is CALL, JMP, Jxx, LOOPx, JCXZ or JECXZ
|
|
* and operand is an immediate value.
|
|
* determine the displacement of jmp;
|
|
* possible return values are:
|
|
* - NOT_ERROR,
|
|
* - ERROR,
|
|
*/
|
|
{
|
|
int_32 addr;
|
|
enum fixup_types fixup_type;
|
|
enum fixup_options fixup_option;
|
|
enum sym_state state;
|
|
struct asym *sym;
|
|
enum memtype mem_type;
|
|
struct dsym *symseg;
|
|
unsigned opidx = IndexFromToken( CodeInfo->token );
|
|
|
|
/* v2.05: just 1 operand possible */
|
|
if ( CurrOpnd != OPND1 ) {
|
|
//EmitError( SYNTAX_ERROR ); /* v2.10: error msg changed */
|
|
return( EmitError( INVALID_INSTRUCTION_OPERANDS ) );
|
|
}
|
|
if ( opndx->explicit && opndx->instr != T_SHORT )
|
|
CodeInfo->mem_type = opndx->mem_type;
|
|
/*
|
|
* Masm checks overrides for branch instructions with immediate operand!
|
|
* Of course, no segment prefix byte is emitted - would be pretty useless.
|
|
* It might cause the call/jmp to become FAR, though.
|
|
*/
|
|
if ( opndx->override != NULL ) {
|
|
segm_override( opndx, NULL );
|
|
DebugMsg(("process_branch(%" I32_SPEC "X): segment override %s\n", GetCurrOffset(), SegOverride ? SegOverride->name : "NULL" ));
|
|
if ( SegOverride && opndx->sym && opndx->sym->segment ) {
|
|
if ( SegOverride != opndx->sym->segment && SegOverride != ((struct dsym *)opndx->sym->segment)->e.seginfo->group ) {
|
|
return( EmitErr( CANNOT_ACCESS_LABEL_THROUGH_SEGMENT_REGISTERS, opndx->sym ? opndx->sym->name : "" ) );
|
|
}
|
|
/* v2.05: switch to far jmp/call */
|
|
if ( SegOverride != &CurrSeg->sym && SegOverride != CurrSeg->e.seginfo->group ) {
|
|
DebugMsg(("process_branch(%" I32_SPEC "X): segment override %s caused FAR jmp/call\n", GetCurrOffset(), SegOverride->name ));
|
|
//CodeInfo->isfar = TRUE;
|
|
CodeInfo->mem_type = MT_FAR;
|
|
}
|
|
}
|
|
}
|
|
|
|
CodeInfo->opnd[OPND1].data32l = opndx->value;
|
|
/* v2.06: make sure, that next bytes are cleared (for OP_I48)! */
|
|
CodeInfo->opnd[OPND1].data32h = 0;
|
|
sym = opndx->sym;
|
|
if( sym == NULL ) { /* no symbolic label specified? */
|
|
DebugMsg(("process_branch(%" I32_SPEC "X): sym=NULL, op.memtype=%Xh\n", GetCurrOffset(), opndx->mem_type ));
|
|
|
|
/* Masm rejects: "jump dest must specify a label */
|
|
#if NEEDLABEL
|
|
return( EmitError( JUMP_DESTINATION_MUST_SPECIFY_A_LABEL ) );
|
|
#else
|
|
if( IS_JMPCALL( CodeInfo->token ) )
|
|
CodeInfo->isfar = TRUE; /* ??? most likely nonsense! */
|
|
|
|
if( CodeInfo->opnd[OPND1].data32l > USHRT_MAX )
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
else
|
|
CodeInfo->opnd_type[OPND1] = OP_I16;
|
|
|
|
return( NOT_ERROR );
|
|
#endif
|
|
}
|
|
DebugMsg1(("process_branch(%" I32_SPEC "X, %s): opnd.explicit=%u/memtype=%X/Ofssize=%u CI.memtype=%X sym.state=%u/mem_type=%Xh/ofs=%" I32_SPEC "X/seg=%s\n",
|
|
GetCurrOffset(), sym->name, opndx->explicit, opndx->mem_type, opndx->Ofssize, CodeInfo->mem_type,
|
|
sym->state, sym->mem_type, sym->offset, sym->segment ? sym->segment->name : "NULL" ));
|
|
|
|
state = sym->state;
|
|
addr = GetCurrOffset(); /* for SYM_UNDEFINED, will force distance to SHORT */
|
|
|
|
/* v2.02: if symbol is GLOBAL and it isn't clear yet were
|
|
* it's located, then assume it is a forward reference (=SYM_UNDEFINED)!
|
|
* This applies to PROTOs and EXTERNDEFs in Pass 1.
|
|
*/
|
|
if ( ( state == SYM_EXTERNAL ) && sym->weak ) {
|
|
DebugMsg1(("process_branch(%s): EXTERNDEF assumed forward reference (=SYM_UNDEFINED)\n", sym->name ));
|
|
state = SYM_UNDEFINED;
|
|
}
|
|
|
|
/* v2.02: removed SYM_UNDEFINED. Don't check segment of such symbols! */
|
|
// if ( state == SYM_UNDEFINED || state == SYM_INTERNAL || state == SYM_EXTERNAL ) {
|
|
if ( state == SYM_INTERNAL || state == SYM_EXTERNAL ) {
|
|
/* v2.04: if the symbol is internal, but wasn't met yet
|
|
* in this pass and its offset is < $, don't use current offset
|
|
*/
|
|
if ( state == SYM_INTERNAL &&
|
|
sym->asmpass != ( Parse_Pass & 0xFF) &&
|
|
sym->offset < addr )
|
|
;
|
|
else
|
|
addr = sym->offset; /* v2.02: init addr, so sym->offset isn't changed */
|
|
symseg = GetSegm( sym );
|
|
if( symseg == NULL || ( CurrSeg != symseg ) ) {
|
|
/* if label has a different segment and jump/call is near or short,
|
|
report an error */
|
|
//if ( ModuleInfo.flatgrp_idx != 0 )
|
|
/* v2.09: make sure there's no near jmp/call between 32- and 64-bit */
|
|
//if ( ModuleInfo.flat_grp )
|
|
if ( ModuleInfo.flat_grp
|
|
#if AMD64_SUPPORT
|
|
&& ( symseg == NULL || symseg->e.seginfo->Ofssize == ModuleInfo.Ofssize )
|
|
#endif
|
|
) {
|
|
} else if ( symseg != NULL && CurrSeg != NULL ) {
|
|
/* if the segments belong to the same group, it's ok */
|
|
if ( symseg->e.seginfo->group != NULL &&
|
|
symseg->e.seginfo->group == CurrSeg->e.seginfo->group )
|
|
;
|
|
/* v2.05: added SegOverride condition */
|
|
//else if ( opndx->mem_type == MT_NEAR ) {
|
|
else if ( opndx->mem_type == MT_NEAR && SegOverride == NULL ) {
|
|
DebugMsg(("process_branch: error, opndx.mem_type is MT_NEAR\n" ));
|
|
return( EmitError( CANNOT_HAVE_IMPLICIT_FAR_JUMP_OR_CALL_TO_NEAR_LABEL ) );
|
|
}
|
|
}
|
|
/* jumps to another segment are just like to another file */
|
|
state = SYM_EXTERNAL;
|
|
}
|
|
} else if ( state != SYM_UNDEFINED ) {
|
|
DebugMsg(("process_branch(%s): error, unexpected symbol state=%u\n", sym->name, sym->state ));
|
|
return( EmitErr( JUMP_DESTINATION_MUST_SPECIFY_A_LABEL ) );
|
|
}
|
|
|
|
if ( state != SYM_EXTERNAL ) {
|
|
/* v1.94: if a segment override is active,
|
|
check if it's matching the assumed value of CS.
|
|
If no, assume a FAR call.
|
|
*/
|
|
if ( SegOverride != NULL && CodeInfo->mem_type == MT_EMPTY ) {
|
|
if ( SegOverride != GetOverrideAssume( ASSUME_CS ) ) {
|
|
CodeInfo->mem_type = MT_FAR;
|
|
}
|
|
}
|
|
if( ( CodeInfo->mem_type == MT_EMPTY ||
|
|
CodeInfo->mem_type == MT_NEAR ) &&
|
|
CodeInfo->isfar == FALSE ) {
|
|
|
|
/* if the label is FAR - or there is a segment override
|
|
* which equals assumed value of CS - and there is no type cast,
|
|
* then do a "far call optimization".
|
|
*/
|
|
if( CodeInfo->token == T_CALL &&
|
|
CodeInfo->mem_type == MT_EMPTY &&
|
|
( sym->mem_type == MT_FAR || SegOverride ) ) {
|
|
DebugMsg1(("process_branch: FAR call optimization applied!\n" ));
|
|
FarCallToNear( CodeInfo ); /* switch mem_type to NEAR */
|
|
}
|
|
|
|
//addr = sym->offset; /* v2.02: this has been done above */
|
|
DebugMsg(("process_branch: step1: addr=%Xh\n", addr ));
|
|
addr -= ( GetCurrOffset() + 2 ); /* calculate the displacement */
|
|
addr += CodeInfo->opnd[OPND1].data32l;
|
|
/* JCXZ, LOOPW, LOOPEW, LOOPZW, LOOPNEW, LOOPNZW,
|
|
JECXZ, LOOPD, LOOPED, LOOPZD, LOOPNED, LOOPNZD? */
|
|
if (( CodeInfo->Ofssize && InstrTable[opidx].byte1_info == F_16A ) ||
|
|
( CodeInfo->Ofssize != USE32 && InstrTable[opidx].byte1_info == F_32A ))
|
|
addr--; /* 1 extra byte for ADRSIZ (0x67) */
|
|
|
|
/* v2.02: removed */
|
|
//if( CodeInfo->token == T_CALL && CodeInfo->mem_type == MT_EMPTY ) {
|
|
// CodeInfo->mem_type = MT_NEAR;
|
|
//}
|
|
DebugMsg(("process_branch: CI.memtype=%Xh addr=%Xh\n", CodeInfo->mem_type, addr ));
|
|
if( CodeInfo->mem_type != MT_NEAR && CodeInfo->token != T_CALL &&
|
|
( addr >= SCHAR_MIN && addr <= SCHAR_MAX ) ) {
|
|
CodeInfo->opnd[OPND1].type = OP_I8;
|
|
} else {
|
|
if ( opndx->instr == T_SHORT || ( IS_XCX_BRANCH( CodeInfo->token ) ) ) {
|
|
/* v2.06: added */
|
|
if( CodeInfo->token == T_CALL ) {
|
|
return( EmitError( CANNOT_USE_SHORT_WITH_CALL ) );
|
|
}
|
|
/* v1.96: since JWasm's backpatch strategy is to move from
|
|
* "smallest" to "largest" distance, an "out of range"
|
|
* error can be detected at any time.
|
|
*/
|
|
DebugMsg(("process_branch: jump out of range, mem_type=%Xh addr=%Xh\n", CodeInfo->mem_type, addr ));
|
|
/* v2.06: removed */
|
|
/* v2.03: added */
|
|
//if ( addr >= SCHAR_MIN && addr <= SCHAR_MAX ) {
|
|
// return( EmitError( ONLY_SHORT_JUMP_DISTANCE_IS_ALLOWED ) );
|
|
//}
|
|
if ( addr < 0 ) {
|
|
addr -= SCHAR_MIN;
|
|
addr = 0 - addr;
|
|
} else
|
|
addr -= SCHAR_MAX;
|
|
return( EmitErr( CodeInfo->mem_type == MT_EMPTY ? JUMP_OUT_OF_RANGE : ONLY_SHORT_JUMP_DISTANCE_IS_ALLOWED, addr ) );
|
|
}
|
|
/* near destination */
|
|
/* is there a type coercion? */
|
|
if ( opndx->Ofssize != USE_EMPTY ) {
|
|
if ( opndx->Ofssize == USE16 ) {
|
|
CodeInfo->opnd[OPND1].type = OP_I16;
|
|
addr -= 1; /* 16 bit displacement */
|
|
} else {
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
addr -= 3; /* 32 bit displacement */
|
|
}
|
|
CodeInfo->prefix.opsiz = OPSIZE( CodeInfo->Ofssize, opndx->Ofssize );
|
|
if ( CodeInfo->prefix.opsiz )
|
|
addr--;
|
|
} else if( CodeInfo->Ofssize > USE16 ) {
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
addr -= 3; /* 32 bit displacement */
|
|
} else {
|
|
CodeInfo->opnd[OPND1].type = OP_I16;
|
|
addr -= 1; /* 16 bit displacement */
|
|
}
|
|
if( IS_CONDJMP( CodeInfo->token ) ) {
|
|
/* 1 extra byte for opcode ( 0F ) */
|
|
addr--;
|
|
}
|
|
}
|
|
|
|
/* store the displacement */
|
|
CodeInfo->opnd[OPND1].data32l = addr;
|
|
DebugMsg1(("process_branch: displacement=%" I32_SPEC "X opnd_type=%" I32_SPEC "X\n", addr, CodeInfo->opnd[OPND1].type ));
|
|
|
|
/* automatic (conditional) jump expansion.
|
|
* for 386 and above this is not needed, since there exists
|
|
* an extended version of Jcc
|
|
*/
|
|
if( ( ModuleInfo.curr_cpu & P_CPU_MASK) < P_386 && IS_JCC( CodeInfo->token ) ) {
|
|
/* look into jump extension */
|
|
if( CodeInfo->opnd[OPND1].type != OP_I8 ) {
|
|
if( CodeInfo->mem_type == MT_EMPTY && ModuleInfo.ljmp == TRUE ) {
|
|
jumpExtend( CodeInfo, FALSE );
|
|
addr -= 1;
|
|
CodeInfo->opnd[OPND1].data32l = addr;
|
|
//return( SCRAP_INSTRUCTION );
|
|
//} else if( !PhaseError ) {
|
|
} else {
|
|
DebugMsg(("%u process_branch: CPU < 386 and Jcc distance != SHORT, mem_type=%X, curr_ofs=%X, addr=%d\n", Parse_Pass + 1, CodeInfo->mem_type, GetCurrOffset(), addr ));
|
|
/* v2.11: don't emit "out of range" if OP_I16 was forced by type coercion ( jmp near ptr xxx ) */
|
|
return( EmitErr( CodeInfo->mem_type == MT_EMPTY ? JUMP_OUT_OF_RANGE : JUMP_DISTANCE_NOT_POSSIBLE, addr ) );
|
|
}
|
|
}
|
|
}
|
|
/* v2.02: in pass one, write "backpatch" fixup for forward
|
|
* references.
|
|
*/
|
|
/* the "if" below needs to be explaind.
|
|
* Fixups will be written for forward references in pass one.
|
|
* state is SYM_UNDEFINED then. The fixups will be scanned when
|
|
* the label is met finally, still in pass one. See backptch.c
|
|
* for details.
|
|
*/
|
|
if ( state != SYM_UNDEFINED )
|
|
return( NOT_ERROR ); /* exit, no fixup is written! */
|
|
}
|
|
}
|
|
|
|
DebugMsg1(("process_branch: fixup needed\n" ));
|
|
|
|
fixup_option = OPTJ_NONE;
|
|
fixup_type = FIX_RELOFF8;
|
|
|
|
mem_type = opndx->mem_type;
|
|
|
|
/* v2.04: far call optimization possible if destination is in
|
|
* another segment of the same group. However, a fixup must be written.
|
|
* Masm does NOT optimize if destination is external!
|
|
*/
|
|
if( CodeInfo->token == T_CALL &&
|
|
CodeInfo->mem_type == MT_EMPTY &&
|
|
( sym->mem_type == MT_FAR || SegOverride ) ) {
|
|
symseg = GetSegm( sym );
|
|
if ( symseg == CurrSeg ||
|
|
( symseg != NULL && symseg->e.seginfo->group != NULL && symseg->e.seginfo->group == CurrSeg->e.seginfo->group ) ) {
|
|
DebugMsg1(("process_branch: FAR call optimization applied!, seg=%X, CurrSeg=%X, grps=%X/%X\n", symseg, CurrSeg, symseg->e.seginfo->group, CurrSeg->e.seginfo->group ));
|
|
FarCallToNear( CodeInfo ); /* switch mem_type to NEAR */
|
|
}
|
|
}
|
|
/* forward ref, or external symbol */
|
|
if( CodeInfo->mem_type == MT_EMPTY && mem_type != MT_EMPTY && opndx->instr != T_SHORT ) {
|
|
/* MT_PROC is most likely obsolete ( used by TYPEDEF only ) */
|
|
/* v2.09: removed */
|
|
//if ( mem_type == MT_PROC )
|
|
// mem_type = ( ( SIZE_CODEPTR & ( 1 << ModuleInfo.model ) ) ? MT_FAR : MT_NEAR );
|
|
switch( mem_type ) {
|
|
case MT_FAR:
|
|
if( IS_JMPCALL( CodeInfo->token ) ) {
|
|
CodeInfo->isfar = TRUE;
|
|
}
|
|
/* v2.06: commented 2 lines to copy behavior of MT_NEAR */
|
|
//CodeInfo->mem_type = mem_type;
|
|
//break;
|
|
case MT_NEAR:
|
|
/* v2.04: 'if' added */
|
|
if ( state != SYM_UNDEFINED )
|
|
CodeInfo->mem_type = mem_type;
|
|
break;
|
|
default:
|
|
DebugMsg(("process_branch: unexpected mem_type %Xh\n", mem_type ));
|
|
/**/myassert( 0 );
|
|
CodeInfo->mem_type = mem_type;
|
|
}
|
|
}
|
|
|
|
/* handle far JMP + CALL? */
|
|
if ( IS_JMPCALL( CodeInfo->token ) &&
|
|
( CodeInfo->isfar == TRUE || CodeInfo->mem_type == MT_FAR )) {
|
|
CodeInfo->isfar = TRUE; /* flag isn't set if explicit is true */
|
|
DebugMsg1(("process_branch: FAR call/jmp\n"));
|
|
switch( CodeInfo->mem_type ) {
|
|
case MT_NEAR:
|
|
if( opndx->explicit || opndx->instr == T_SHORT ) {
|
|
return( EmitError( CANNOT_USE_SHORT_OR_NEAR ) );
|
|
}
|
|
/* fall through */
|
|
case MT_FAR:
|
|
case MT_EMPTY:
|
|
/* v1.95: explicit flag to be removed! */
|
|
//if ( opndx->explicit && opndx->Ofssize != USE_EMPTY )
|
|
if ( opndx->Ofssize != USE_EMPTY )
|
|
CodeInfo->prefix.opsiz = OPSIZE( CodeInfo->Ofssize, opndx->Ofssize );
|
|
else
|
|
CodeInfo->prefix.opsiz = OPSIZE( CodeInfo->Ofssize, GetSymOfssize( sym ) );
|
|
|
|
/* set fixup frame variables Frame + Frame_Datum */
|
|
set_frame( sym );
|
|
if( IS_OPER_32( CodeInfo ) ) {
|
|
fixup_type = FIX_PTR32;
|
|
CodeInfo->opnd[OPND1].type = OP_I48;
|
|
} else {
|
|
fixup_type = FIX_PTR16;
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
}
|
|
break;
|
|
#ifdef DEBUG_OUT
|
|
default: /* cannot happen */
|
|
DebugMsg(("process_branch: JMP/CALL far, unexpected mem_type=%X\n", CodeInfo->mem_type ));
|
|
/**/myassert( 0 );
|
|
//return( EmitError( INVALID_OPERAND_SIZE ) );
|
|
#endif
|
|
}
|
|
CodeInfo->opnd[OPND1].InsFixup = CreateFixup( sym, fixup_type, fixup_option );
|
|
return( NOT_ERROR );
|
|
} /* end if FAR JMP/CALL */
|
|
|
|
switch( CodeInfo->token ) {
|
|
case T_CALL:
|
|
if( opndx->instr == T_SHORT ) {
|
|
return( EmitError( CANNOT_USE_SHORT_WITH_CALL ) );
|
|
}
|
|
if( CodeInfo->mem_type == MT_EMPTY ) {
|
|
fixup_option = OPTJ_CALL;
|
|
if( CodeInfo->Ofssize > USE16 ) {
|
|
fixup_type = FIX_RELOFF32;
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
} else {
|
|
fixup_type = FIX_RELOFF16;
|
|
CodeInfo->opnd[OPND1].type = OP_I16;
|
|
}
|
|
break;
|
|
}
|
|
/* fall through */
|
|
case T_JMP:
|
|
DebugMsg1(("process_branch: JMP/CALL, CodeInfo->memtype=%X\n", CodeInfo->mem_type ));
|
|
switch( CodeInfo->mem_type ) {
|
|
case MT_EMPTY:
|
|
/* forward reference
|
|
* default distance is short, we will expand later if needed
|
|
*/
|
|
CodeInfo->opnd[OPND1].type = OP_I8;
|
|
fixup_type = FIX_RELOFF8;
|
|
fixup_option = (opndx->instr == T_SHORT) ? OPTJ_EXPLICIT : OPTJ_NONE;
|
|
break;
|
|
case MT_NEAR:
|
|
fixup_option = OPTJ_EXPLICIT;
|
|
#if 1 /* v2.11: added, see call5.aso */
|
|
if( opndx->Ofssize != USE_EMPTY ) {
|
|
if ( opndx->Ofssize == USE16 ) {
|
|
fixup_type = FIX_RELOFF16;
|
|
CodeInfo->opnd[OPND1].type = OP_I16;
|
|
} else {
|
|
fixup_type = FIX_RELOFF32;
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
}
|
|
CodeInfo->prefix.opsiz = OPSIZE( CodeInfo->Ofssize, opndx->Ofssize );
|
|
} else
|
|
#endif
|
|
if( CodeInfo->Ofssize > USE16 ) {
|
|
fixup_type = FIX_RELOFF32;
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
} else {
|
|
fixup_type = FIX_RELOFF16;
|
|
CodeInfo->opnd[OPND1].type = OP_I16;
|
|
}
|
|
set_frame( sym );/* added v1.95 (after change in fixup.c */
|
|
break;
|
|
#ifdef DEBUG_OUT
|
|
default:
|
|
DebugMsg(("process_branch: JMP/CALL, unexpected mem_type=%X\n", CodeInfo->mem_type ));
|
|
/**/myassert( 0 );
|
|
//return( EmitError( INVALID_OPERAND_SIZE ) );
|
|
#endif
|
|
}
|
|
/* deactivated because there's no override involved here */
|
|
// check_assume( sym, EMPTY );
|
|
break;
|
|
default: /* JxCXZ, LOOPxx, Jxx */
|
|
/* JxCXZ and LOOPxx always require SHORT label */
|
|
if ( IS_XCX_BRANCH( CodeInfo->token ) ) {
|
|
if( CodeInfo->mem_type != MT_EMPTY && opndx->instr != T_SHORT ) {
|
|
return( EmitError( ONLY_SHORT_JUMP_DISTANCE_IS_ALLOWED ) );
|
|
}
|
|
CodeInfo->opnd[OPND1].type = OP_I8;
|
|
fixup_option = OPTJ_EXPLICIT;
|
|
fixup_type = FIX_RELOFF8;
|
|
break;
|
|
}
|
|
/* just Jxx remaining */
|
|
|
|
if( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
|
|
|
|
switch( CodeInfo->mem_type ) {
|
|
case MT_EMPTY:
|
|
/* forward reference */
|
|
fixup_option = ( opndx->instr == T_SHORT ) ? OPTJ_EXPLICIT : OPTJ_JXX;
|
|
fixup_type = FIX_RELOFF8;
|
|
CodeInfo->opnd[OPND1].type = OP_I8;
|
|
break;
|
|
case MT_NEAR:
|
|
fixup_option = OPTJ_EXPLICIT;
|
|
/* v1.95: explicit flag to be removed! */
|
|
//if ( opndx->explicit && opndx->Ofssize != USE_EMPTY ) {
|
|
if ( opndx->Ofssize != USE_EMPTY ) {
|
|
CodeInfo->prefix.opsiz = OPSIZE( CodeInfo->Ofssize, opndx->Ofssize );
|
|
CodeInfo->opnd[OPND1].type = (opndx->Ofssize >= USE32) ? OP_I32 : OP_I16;
|
|
} else if( CodeInfo->Ofssize > USE16 ) {
|
|
fixup_type = FIX_RELOFF32;
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
} else {
|
|
fixup_type = FIX_RELOFF16;
|
|
CodeInfo->opnd[OPND1].type = OP_I16;
|
|
}
|
|
break;
|
|
case MT_FAR:
|
|
if ( ModuleInfo.ljmp ) { /* OPTION LJMP set? */
|
|
/* v1.95: explicit flag to be removed! */
|
|
//if ( opndx->explicit && opndx->Ofssize != USE_EMPTY )
|
|
if ( opndx->Ofssize != USE_EMPTY )
|
|
CodeInfo->prefix.opsiz = OPSIZE( CodeInfo->Ofssize, opndx->Ofssize );
|
|
else
|
|
CodeInfo->prefix.opsiz = OPSIZE( CodeInfo->Ofssize, GetSymOfssize( sym ));
|
|
/* destination is FAR (externdef <dest>:far */
|
|
DebugMsg(("process_branch: JMP/CALL, FAR memtype, jump extension\n" ));
|
|
jumpExtend( CodeInfo, TRUE );
|
|
CodeInfo->isfar = TRUE;
|
|
if( IS_OPER_32( CodeInfo ) ) {
|
|
fixup_type = FIX_PTR32;
|
|
CodeInfo->opnd[OPND1].type = OP_I48;
|
|
} else {
|
|
fixup_type = FIX_PTR16;
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
}
|
|
break;
|
|
}
|
|
/* fall through */
|
|
default: /* is another memtype possible at all? */
|
|
return( EmitError( ONLY_SHORT_AND_NEAR_JUMP_DISTANCE_IS_ALLOWED ) );
|
|
}
|
|
} else {
|
|
/* the only mode in 8086, 80186, 80286 is
|
|
* Jxx SHORT
|
|
* Masm allows "Jxx near" if LJMP is on (default) */
|
|
switch( CodeInfo->mem_type ) {
|
|
case MT_EMPTY:
|
|
if ( opndx->instr == T_SHORT )
|
|
fixup_option = OPTJ_EXPLICIT;
|
|
else
|
|
fixup_option = OPTJ_EXTEND;
|
|
fixup_type = FIX_RELOFF8;
|
|
CodeInfo->opnd[OPND1].type = OP_I8;
|
|
break;
|
|
case MT_NEAR: /* allow Jxx NEAR if LJMP on */
|
|
case MT_FAR:
|
|
if ( ModuleInfo.ljmp ) {
|
|
if ( CodeInfo->mem_type == MT_FAR ) {
|
|
jumpExtend( CodeInfo, TRUE );
|
|
fixup_type = FIX_PTR16;
|
|
CodeInfo->isfar = TRUE;
|
|
CodeInfo->opnd[OPND1].type = OP_I32;
|
|
} else {
|
|
jumpExtend( CodeInfo, FALSE );
|
|
fixup_type = FIX_RELOFF16;
|
|
CodeInfo->opnd[OPND1].type = OP_I16;
|
|
}
|
|
break;
|
|
}
|
|
/* fall through */
|
|
default:
|
|
return( EmitError( ONLY_SHORT_JUMP_DISTANCE_IS_ALLOWED ) );
|
|
}
|
|
}
|
|
} /* end switch (CodeInfo->token) */
|
|
CodeInfo->opnd[OPND1].InsFixup = CreateFixup( sym, fixup_type, fixup_option );
|
|
return( NOT_ERROR );
|
|
}
|