jwasm/cpumodel.c
2014-06-18 19:16:56 +04:00

495 lines
16 KiB
C

/****************************************************************************
*
* This code is Public Domain.
*
* ========================================================================
*
* Description: processes .MODEL and cpu directives
*
****************************************************************************/
#include <ctype.h>
#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "segment.h"
#include "assume.h"
#include "equate.h"
#include "lqueue.h"
#include "tokenize.h"
#include "expreval.h"
#include "fastpass.h"
#include "listing.h"
#include "proc.h"
#include "macro.h"
#include "fixup.h"
#ifdef DEBUG_OUT
#include "reswords.h"
#endif
#include "myassert.h"
#if PE_SUPPORT
#include "bin.h"
#endif
#define DOT_XMMARG 0 /* 1=optional argument for .XMM directive */
extern const char szDgroup[];
/* prototypes */
/* the following flags assume the MODEL_xxx enumeration.
* must be sorted like MODEL_xxx enum:
* TINY=1, SMALL=2, COMPACT=3, MEDIUM=4, LARGE=5, HUGE=6, FLAT=7
*/
const char * const ModelToken[] = {
"TINY", "SMALL", "COMPACT", "MEDIUM", "LARGE", "HUGE", "FLAT" };
#define INIT_LANG 0x1
#define INIT_STACK 0x2
#define INIT_OS 0x4
struct typeinfo {
uint_8 value; /* value assigned to the token */
uint_8 init; /* kind of token */
};
static const char * const ModelAttr[] = {
"NEARSTACK", "FARSTACK", "OS_OS2", "OS_DOS" };
static const struct typeinfo ModelAttrValue[] = {
{ STACK_NEAR, INIT_STACK },
{ STACK_FAR, INIT_STACK },
{ OPSYS_DOS, INIT_OS },
{ OPSYS_OS2, INIT_OS },
};
static struct asym *sym_CodeSize ; /* numeric. requires model */
static struct asym *sym_DataSize ; /* numeric. requires model */
static struct asym *sym_Model ; /* numeric. requires model */
struct asym *sym_Interface ; /* numeric. requires model */
struct asym *sym_Cpu ; /* numeric. This is ALWAYS set */
#if AMD64_SUPPORT
#if COFF_SUPPORT
const struct format_options coff64_fmtopt = { NULL, COFF64_DISALLOWED, "PE32+" };
#endif
#if ELF_SUPPORT
const struct format_options elf64_fmtopt = { NULL, ELF64_DISALLOWED, "ELF64" };
#endif
#endif
/* find token in a string table */
static int FindToken( const char *token, const char * const *table, int size )
/****************************************************************************/
{
int i;
for( i = 0; i < size; i++, table++ ) {
if( _stricmp( *table, token ) == 0 ) {
return( i );
}
}
return( -1 ); /* Not found */
}
static struct asym *AddPredefinedConstant( const char *name, int value )
/**********************************************************************/
{
struct asym *sym = CreateVariable( name, value );
if (sym)
sym->predefined = TRUE;
return(sym);
}
/* set default wordsize for segment definitions */
static ret_code SetDefaultOfssize( int size )
/*******************************************/
{
/* outside any segments? */
if( CurrSeg == NULL ) {
ModuleInfo.defOfssize = size;
}
return( SetOfssize() );
}
/* set memory model, called by ModelDirective()
* also set predefined symbols:
* - @CodeSize (numeric)
* - @code (text)
* - @DataSize (numeric)
* - @data (text)
* - @stack (text)
* - @Model (numeric)
* - @Interface (numeric)
* inactive:
* - @fardata (text)
* - @fardata? (text)
* Win64 only:
* - @ReservedStack (numeric)
*/
static void SetModel( void )
/**************************/
{
int value;
const char *textvalue;
//struct asym *sym;
DebugMsg1(("SetModel() enter (model=%u)\n", ModuleInfo.model ));
/* if model is set, it disables OT_SEGMENT of -Zm switch */
if ( ModuleInfo.model == MODEL_FLAT ) {
ModuleInfo.offsettype = OT_FLAT;
#if AMD64_SUPPORT
SetDefaultOfssize( ((ModuleInfo.curr_cpu & P_CPU_MASK) >= P_64 ) ? USE64 : USE32 );
/* v2.03: if cpu is x64 and language is fastcall,
* set fastcall type to win64.
* This is rather hackish, but currently there's no other possibility
* to enable the win64 ABI from the source.
*/
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) == P_64 )
if ( ModuleInfo.langtype == LANG_FASTCALL ) {
if ( Options.output_format != OFORMAT_ELF ) {
DebugMsg(("SetModel: FASTCALL type set to WIN64\n"));
ModuleInfo.fctype = FCT_WIN64;
}
}
#else
SetDefaultOfssize( USE32 );
#endif
/* v2.11: define symbol FLAT - after default offset size has been set! */
DefineFlatGroup();
} else
ModuleInfo.offsettype = OT_GROUP;
ModelSimSegmInit( ModuleInfo.model ); /* create segments in first pass */
ModelAssumeInit();
if ( ModuleInfo.list )
LstWriteSrcLine();
RunLineQueue();
if ( Parse_Pass != PASS_1 )
return;
/* Set @CodeSize */
if ( SIZE_CODEPTR & ( 1 << ModuleInfo.model ) ) {
value = 1;
/* v2.06: SimpleType[] is obsolete */
//SimpleType[ST_PROC].mem_type = MT_FAR;
} else {
value = 0;
// SimpleType[ST_PROC].mem_type = MT_NEAR; /* this is default */
}
sym_CodeSize = AddPredefinedConstant( "@CodeSize", value );
AddPredefinedText( "@code", SimGetSegName( SIM_CODE ) );
/* Set @DataSize */
switch( ModuleInfo.model ) {
case MODEL_COMPACT:
case MODEL_LARGE:
value = 1;
break;
case MODEL_HUGE:
value = 2;
break;
default:
value = 0;
break;
}
sym_DataSize = AddPredefinedConstant( "@DataSize", value );
textvalue = ( ModuleInfo.model == MODEL_FLAT ? "FLAT" : szDgroup );
AddPredefinedText( "@data", textvalue );
if ( ModuleInfo.distance == STACK_FAR )
textvalue = "STACK";
AddPredefinedText( "@stack", textvalue );
#if 0
AddPredefinedText( "@fardata", ( ModuleInfo.model == MODEL_FLAT ? "FLAT" : SimGetSegName( SIM_FARDATA ) ) );
AddPredefinedText( "@fardata?", ( ModuleInfo.model == MODEL_FLAT ? "FLAT" : SimGetSegName( SIM_FARDATA_UN ) ) );
#endif
/* Set @Model and @Interface */
sym_Model = AddPredefinedConstant( "@Model", ModuleInfo.model );
sym_Interface = AddPredefinedConstant( "@Interface", ModuleInfo.langtype );
#if AMD64_SUPPORT
if ( ModuleInfo.defOfssize == USE64 && ModuleInfo.fctype == FCT_WIN64 ) {
sym_ReservedStack = AddPredefinedConstant( "@ReservedStack", 0 );
}
#endif
#if PE_SUPPORT
if ( ModuleInfo.sub_format == SFORMAT_PE )
pe_create_PE_header();
#endif
#ifdef DEBUG_OUT
if ( Options.dump_reswords )
DumpResWords();
#endif
}
/* handle .model directive
* syntax: .MODEL <FLAT|TINY|SMALL...> [,<C|PASCAL|STDCALL...>][,<NEARSTACK|FARSTACK>][,<OS_DOS|OS_OS2>]
* sets fields
* - ModuleInfo.model
* - ModuleInfo.language
* - ModuleInfo.distance
* - ModuleInfo.ostype
* if model is FLAT, defines FLAT pseudo-group
* set default segment names for code and data
*/
ret_code ModelDirective( int i, struct asm_tok tokenarray[] )
/***********************************************************/
{
enum model_type model;
enum lang_type language;
enum dist_type distance;
enum os_type ostype;
int index;
uint_8 init;
uint_8 initv;
DebugMsg1(("ModelDirective enter\n"));
/* v2.03: it may occur that "code" is defined BEFORE the MODEL
* directive (i.e. DB directives in AT-segments). For FASTPASS,
* this may have caused errors because contents of the ModuleInfo
* structure was saved before the .MODEL directive.
*/
//if( Parse_Pass != PASS_1 ) {
if( Parse_Pass != PASS_1 && ModuleInfo.model != MODEL_NONE ) {
/* just set the model with SetModel() if pass is != 1.
* This won't set the language ( which can be modified by
* OPTION LANGUAGE directive ), but the language in ModuleInfo
* isn't needed anymore once pass one is done.
*/
SetModel();
return( NOT_ERROR );
}
i++;
if ( tokenarray[i].token == T_FINAL ) {
return( EmitError( EXPECTED_MEMORY_MODEL ) );
}
/* get the model argument */
index = FindToken( tokenarray[i].string_ptr, ModelToken, sizeof( ModelToken )/sizeof( ModelToken[0] ) );
if( index >= 0 ) {
if( ModuleInfo.model != MODEL_NONE ) {
//if ( Parse_Pass == PASS_1 ) /* not needed, this code runs in pass one only */
EmitWarn( 2, MODEL_DECLARED_ALREADY );
}
model = index + 1; /* model is one-base ( 0 is MODEL_NONE ) */
i++;
} else {
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) );
}
/* get the optional arguments: language, stack distance, os */
init = 0;
while ( i < ( Token_Count - 1 ) && tokenarray[i].token == T_COMMA ) {
i++;
if ( tokenarray[i].token != T_COMMA ) {
if ( GetLangType( &i, tokenarray, &language ) == NOT_ERROR ) {
initv = INIT_LANG;
} else {
index = FindToken( tokenarray[i].string_ptr, ModelAttr, sizeof( ModelAttr )/sizeof( ModelAttr[0] ) );
if ( index < 0 )
break;
initv = ModelAttrValue[index].init;
switch ( initv ) {
case INIT_STACK:
if ( model == MODEL_FLAT ) {
return( EmitError( INVALID_MODEL_PARAM_FOR_FLAT ) );
}
distance = ModelAttrValue[index].value;
break;
case INIT_OS:
ostype = ModelAttrValue[index].value;
break;
}
i++;
}
/* attribute set already? */
if ( initv & init ) {
i--;
break;
}
init |= initv;
}
}
/* everything parsed successfully? */
if ( tokenarray[i].token != T_FINAL ) {
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) );
}
if ( model == MODEL_FLAT ) {
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK) < P_386 ) {
return( EmitError( INSTRUCTION_OR_REGISTER_NOT_ACCEPTED_IN_CURRENT_CPU_MODE ) );
}
#if AMD64_SUPPORT
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_64 ) /* cpu 64-bit? */
switch ( Options.output_format ) {
case OFORMAT_COFF: ModuleInfo.fmtopt = &coff64_fmtopt; break;
case OFORMAT_ELF: ModuleInfo.fmtopt = &elf64_fmtopt; break;
};
#endif
/* v2.11: define FLAT symbol is to early here, because defOfssize isn't set yet */
//DefineFlatGroup();
}
ModuleInfo.model = model;
if ( init & INIT_LANG )
ModuleInfo.langtype = language;
if ( init & INIT_STACK )
ModuleInfo.distance = distance;
if ( init & INIT_OS )
ModuleInfo.ostype = ostype;
SetModelDefaultSegNames();
SetModel();
return( NOT_ERROR );
}
/* set CPU and FPU parameter in ModuleInfo.cpu + ModuleInfo.curr_cpu.
* ModuleInfo.cpu is the value of Masm's @CPU symbol.
* ModuleInfo.curr_cpu is the old OW Wasm value.
* additional notes:
* .[1|2|3|4|5|6]86 will reset .MMX, .K3D and .XMM,
* OTOH, .MMX/.XMM won't automatically enable .586/.686 ( Masm does! )
*/
ret_code SetCPU( enum cpu_info newcpu )
/*************************************/
{
int temp;
DebugMsg1(("SetCPU(%X) enter\n", newcpu ));
if ( newcpu == P_86 || ( newcpu & P_CPU_MASK ) ) {
/* reset CPU and EXT bits */
ModuleInfo.curr_cpu &= ~( P_CPU_MASK | P_EXT_MASK | P_PM );
/* set CPU bits */
ModuleInfo.curr_cpu |= newcpu & ( P_CPU_MASK | P_PM );
/* set default FPU bits if nothing is given and .NO87 not active */
if ( (ModuleInfo.curr_cpu & P_FPU_MASK) != P_NO87 &&
( newcpu & P_FPU_MASK ) == 0 ) {
ModuleInfo.curr_cpu &= ~P_FPU_MASK;
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_286 )
ModuleInfo.curr_cpu |= P_87;
else if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_386 )
ModuleInfo.curr_cpu |= P_287;
else
ModuleInfo.curr_cpu |= P_387;
}
}
if( newcpu & P_FPU_MASK ) {
ModuleInfo.curr_cpu &= ~P_FPU_MASK;
ModuleInfo.curr_cpu |= (newcpu & P_FPU_MASK);
}
#if AMD64_SUPPORT
/* enable MMX, K3D, SSEx for 64bit cpus */
if ( ( newcpu & P_CPU_MASK ) == P_64 )
ModuleInfo.curr_cpu |= P_EXT_ALL;
#endif
if( newcpu & P_EXT_MASK ) {
ModuleInfo.curr_cpu &= ~P_EXT_MASK;
ModuleInfo.curr_cpu |= (newcpu & P_EXT_MASK);
}
/* set the Masm compatible @Cpu value */
temp = ModuleInfo.curr_cpu & P_CPU_MASK;
switch ( temp ) {
case P_186: ModuleInfo.cpu = M_8086 | M_186; break;
case P_286: ModuleInfo.cpu = M_8086 | M_186 | M_286; break;
case P_386: ModuleInfo.cpu = M_8086 | M_186 | M_286 | M_386; break;
case P_486: ModuleInfo.cpu = M_8086 | M_186 | M_286 | M_386 | M_486; break;
case P_586: ModuleInfo.cpu = M_8086 | M_186 | M_286 | M_386 | M_486 | M_586; break;
/* Masm's .686 directive doesn't set the Pentium flag! A bug? */
//case P_686: ModuleInfo.cpu = M_8086 | M_186 | M_286 | M_386 | M_486 | M_586 | M_686; break;
#if AMD64_SUPPORT
case P_64:
#endif
case P_686: ModuleInfo.cpu = M_8086 | M_186 | M_286 | M_386 | M_486 | M_686; break;
default: ModuleInfo.cpu = M_8086; break;
}
if ( ModuleInfo.curr_cpu & P_PM )
ModuleInfo.cpu = ModuleInfo.cpu | M_PROT;
temp = ModuleInfo.curr_cpu & P_FPU_MASK;
switch (temp) {
case P_87: ModuleInfo.cpu = ModuleInfo.cpu | M_8087; break;
case P_287: ModuleInfo.cpu = ModuleInfo.cpu | M_8087 | M_287; break;
case P_387: ModuleInfo.cpu = ModuleInfo.cpu | M_8087 | M_287 | M_387; break;
}
DebugMsg1(("SetCPU: ModuleInfo.curr_cpu=%X, @Cpu=%X\n", ModuleInfo.curr_cpu, ModuleInfo.cpu ));
//MakeCPUConstant( newcpu );
if ( ModuleInfo.model == MODEL_NONE )
#if AMD64_SUPPORT
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK) >= P_64 ) {
SetDefaultOfssize( USE64 );
} else
#endif
SetDefaultOfssize( ((ModuleInfo.curr_cpu & P_CPU_MASK) >= P_386) ? USE32 : USE16 );
/* Set @Cpu */
/* differs from Codeinfo cpu setting */
sym_Cpu = CreateVariable( "@Cpu", ModuleInfo.cpu );
return( NOT_ERROR );
}
/* handles
.8086,
.[1|2|3|4|5|6]86[p],
.8087,
.[2|3]87,
.NO87, .MMX, .K3D, .XMM directives.
*/
ret_code CpuDirective( int i, struct asm_tok tokenarray[] )
/*********************************************************/
{
enum cpu_info newcpu;
//newcpu = comp_opt( tokenarray[i].tokval );
newcpu = GetSflagsSp( tokenarray[i].tokval );
#if DOT_XMMARG
.if ( tokenarray[i].tokval == T_DOT_XMM && tokenarray[i+1].token != T_FINAL ) {
struct expr opndx;
i++;
if ( EvalOperand( &i, Token_Count, &opndx, 0 ) == ERROR )
return( ERROR );
if ( opndx.kind != EXPR_CONST || opndx.value < 1 || opndx.value > 4 ) {
return( EmitConstError( &opndx ) );
}
newcpy &= ~P_SSEALL;
switch ( opndx.value ) {
case 4: newcpy |= P_SSE4;
case 3: newcpy |= P_SSE3|P_SSSE3;
case 2: newcpy |= P_SSE2;
case 1: newcpy |= P_SSE1; break;
}
} else
#endif
i++;
if ( tokenarray[i].token != T_FINAL ) {
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) );
}
return( SetCPU( newcpu ) );
}