mirror of
https://github.com/NishiOwO/JWasm.git
synced 2025-04-21 16:54:39 +00:00
1529 lines
48 KiB
C
1529 lines
48 KiB
C
/****************************************************************************
|
|
*
|
|
* This code is Public Domain.
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* Description: assemble a module.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
|
|
#include "globals.h"
|
|
#include "memalloc.h"
|
|
#include "input.h"
|
|
#include "parser.h"
|
|
#include "reswords.h"
|
|
#include "tokenize.h"
|
|
#include "condasm.h"
|
|
#include "segment.h"
|
|
#include "assume.h"
|
|
#include "proc.h"
|
|
#include "expreval.h"
|
|
#include "hll.h"
|
|
#include "context.h"
|
|
#include "types.h"
|
|
#include "label.h"
|
|
#include "macro.h"
|
|
#include "extern.h"
|
|
#include "fixup.h"
|
|
#include "omf.h"
|
|
#include "fastpass.h"
|
|
#include "listing.h"
|
|
#include "msgtext.h"
|
|
#include "myassert.h"
|
|
#include "linnum.h"
|
|
#include "cpumodel.h"
|
|
#include "lqueue.h"
|
|
#if DLLIMPORT
|
|
#include "mangle.h"
|
|
#endif
|
|
|
|
#if COFF_SUPPORT
|
|
#include "coff.h"
|
|
#endif
|
|
#if ELF_SUPPORT
|
|
#include "elf.h"
|
|
#endif
|
|
#if BIN_SUPPORT
|
|
#include "bin.h"
|
|
#endif
|
|
|
|
#if 1 //def __SW_BD
|
|
#include <setjmp.h>
|
|
jmp_buf jmpenv;
|
|
#endif
|
|
|
|
#ifdef __SW_BD
|
|
#define EXPQUAL __stdcall
|
|
#else
|
|
#define EXPQUAL
|
|
#endif
|
|
|
|
#define USELSLINE 1 /* must match switch in listing.c! */
|
|
|
|
//#define ASM_EXT "asm"
|
|
#ifdef __UNIX__
|
|
#define OBJ_EXT "o"
|
|
#else
|
|
#define OBJ_EXT "obj"
|
|
#endif
|
|
#define LST_EXT "lst"
|
|
#define ERR_EXT "err"
|
|
#define BIN_EXT "BIN"
|
|
#define EXE_EXT "EXE"
|
|
|
|
extern int_32 LastCodeBufSize;
|
|
extern char *DefaultDir[NUM_FILE_TYPES];
|
|
extern const char *ModelToken[];
|
|
#if FASTMEM==0
|
|
extern void FreeLibQueue();
|
|
#endif
|
|
|
|
/* parameters for output formats. order must match enum oformat */
|
|
static const struct format_options formatoptions[] = {
|
|
#if BIN_SUPPORT
|
|
{ bin_init, BIN_DISALLOWED, "BIN" },
|
|
#endif
|
|
{ omf_init, OMF_DISALLOWED, "OMF" },
|
|
#if COFF_SUPPORT
|
|
{ coff_init, COFF32_DISALLOWED, "COFF" },
|
|
#endif
|
|
#if ELF_SUPPORT
|
|
{ elf_init, ELF32_DISALLOWED, "ELF" },
|
|
#endif
|
|
};
|
|
|
|
struct module_info ModuleInfo;
|
|
unsigned int Parse_Pass; /* assembly pass */
|
|
//unsigned int GeneratedCode; /* v2.10: moved to ModuleInfo */
|
|
struct qdesc LinnumQueue; /* queue of line_num_info items */
|
|
|
|
bool write_to_file; /* write object module */
|
|
|
|
|
|
#if 0
|
|
/* for OW, it would be good to remove the CharUpperA() emulation
|
|
* implemented in apiemu.c. Unfortunately, OW isn't happy with
|
|
* a local, simple version of _strupr() - it still wants to
|
|
* import CharUpperA.
|
|
*/
|
|
char * _strupr( char *src )
|
|
{
|
|
char *dst;
|
|
for ( dst = src; *dst; dst++ )
|
|
if ( *dst >= 'a' && *dst <= 'z' )
|
|
*dst &= ~0x20;
|
|
return( src );
|
|
}
|
|
#endif
|
|
|
|
#if COFF_SUPPORT || PE_SUPPORT
|
|
|
|
/* struct to help convert section names in COFF, ELF, PE */
|
|
struct conv_section {
|
|
uint_8 len;
|
|
uint_8 flags; /* see below */
|
|
const char *src;
|
|
const char *dst;
|
|
};
|
|
enum cvs_flags {
|
|
CSF_GRPCHK = 1
|
|
};
|
|
|
|
enum conv_section_index {
|
|
CSI_TEXT = 0,
|
|
CSI_DATA,
|
|
CSI_CONST,
|
|
CSI_BSS
|
|
};
|
|
|
|
/* order must match enum conv_section_index above */
|
|
static const struct conv_section cst[] = {
|
|
{ 5, CSF_GRPCHK, "_TEXT", ".text" },
|
|
{ 5, CSF_GRPCHK, "_DATA", ".data" },
|
|
{ 5, CSF_GRPCHK, "CONST", ".rdata" },
|
|
{ 4, 0, "_BSS", ".bss" }
|
|
};
|
|
|
|
/* order must match enum conv_section_index above */
|
|
static const enum seg_type stt[] = {
|
|
SEGTYPE_CODE, SEGTYPE_DATA, SEGTYPE_DATA, SEGTYPE_BSS
|
|
};
|
|
|
|
/*
|
|
* translate section names (COFF+PE):
|
|
* _TEXT -> .text
|
|
* _DATA -> .data
|
|
* CONST -> .rdata
|
|
* _BSS -> .bss
|
|
*/
|
|
|
|
char *ConvertSectionName( const struct asym *sym, enum seg_type *pst, char *buffer )
|
|
/**********************************************************************************/
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < sizeof( cst ) / sizeof( cst[0] ); i++ ) {
|
|
if ( memcmp( sym->name, cst[i].src, cst[i].len ) == 0 ) {
|
|
if ( sym->name[cst[i].len] == NULLC || ( sym->name[cst[i].len] == '$' && ( cst[i].flags & CSF_GRPCHK ) ) ) {
|
|
|
|
if ( pst ) {
|
|
if ( i == CSI_BSS && ( (struct dsym *)sym)->e.seginfo->bytes_written != 0 )
|
|
; /* don't set segment type to BSS if the segment contains initialized data */
|
|
else
|
|
*pst = stt[i];
|
|
}
|
|
|
|
if ( sym->name[cst[i].len] == NULLC ) {
|
|
#if DJGPP_SUPPORT
|
|
/* DJGPP won't be happy with .rdata segment name */
|
|
if( ModuleInfo.sub_format == SFORMAT_DJGPP && i == CSI_CONST )
|
|
return( ".const" );
|
|
#endif
|
|
return( (char *)cst[i].dst );
|
|
}
|
|
|
|
strcpy( buffer, cst[i].dst );
|
|
strcat( buffer, sym->name+cst[i].len );
|
|
return( buffer );
|
|
}
|
|
}
|
|
}
|
|
return( sym->name );
|
|
}
|
|
#endif
|
|
|
|
/* Write a byte to the segment buffer.
|
|
* in OMF, the segment buffer is flushed when the max. record size is reached.
|
|
*/
|
|
void OutputByte( unsigned char byte )
|
|
/***********************************/
|
|
{
|
|
if( write_to_file == TRUE ) {
|
|
uint_32 idx = CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc;
|
|
#ifdef DEBUG_OUT
|
|
if ( CurrSeg->e.seginfo->current_loc < CurrSeg->e.seginfo->start_loc ) {
|
|
//_asm int 3;
|
|
}
|
|
#endif
|
|
/**/myassert( CurrSeg->e.seginfo->current_loc >= CurrSeg->e.seginfo->start_loc );
|
|
if( Options.output_format == OFORMAT_OMF && idx >= MAX_LEDATA_THRESHOLD ) {
|
|
omf_FlushCurrSeg();
|
|
idx = CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc;
|
|
}
|
|
//DebugMsg(("OutputByte: buff=%p, idx=%" I32_SPEC "X, byte=%X, codebuff[0]=%X\n", CurrSeg->e.seginfo->CodeBuffer, idx, byte, *CurrSeg->e.seginfo->CodeBuffer ));
|
|
CurrSeg->e.seginfo->CodeBuffer[idx] = byte;
|
|
}
|
|
#if 1
|
|
/* check this in pass 1 only */
|
|
else if( CurrSeg->e.seginfo->current_loc < CurrSeg->e.seginfo->start_loc ) {
|
|
DebugMsg(("OutputByte: segment start loc changed from %" I32_SPEC "Xh to %" I32_SPEC "Xh\n",
|
|
CurrSeg->e.seginfo->start_loc,
|
|
CurrSeg->e.seginfo->current_loc));
|
|
CurrSeg->e.seginfo->start_loc = CurrSeg->e.seginfo->current_loc;
|
|
}
|
|
#endif
|
|
CurrSeg->e.seginfo->current_loc++;
|
|
CurrSeg->e.seginfo->bytes_written++;
|
|
CurrSeg->e.seginfo->written = TRUE;
|
|
if( CurrSeg->e.seginfo->current_loc > CurrSeg->sym.max_offset )
|
|
CurrSeg->sym.max_offset = CurrSeg->e.seginfo->current_loc;
|
|
}
|
|
|
|
#if 0 /* v2.03: OutputCodeByte is obsolete */
|
|
void OutputCodeByte( unsigned char byte )
|
|
/***************************************/
|
|
{
|
|
// if ( ModuleInfo.CommentDataInCode )
|
|
// omf_OutSelect( FALSE );
|
|
OutputByte( byte );
|
|
}
|
|
#endif
|
|
|
|
void FillDataBytes( unsigned char byte, int len )
|
|
/***********************************************/
|
|
{
|
|
if ( ModuleInfo.CommentDataInCode )
|
|
omf_OutSelect( TRUE );
|
|
for( ; len; len-- )
|
|
OutputByte( byte );
|
|
}
|
|
|
|
/*
|
|
* this function is to output (small, <= 8) amounts of bytes which must
|
|
* not be separated ( for omf, because of fixups )
|
|
*/
|
|
|
|
void OutputBytes( const unsigned char *pbytes, int len, struct fixup *fixup )
|
|
/***************************************************************************/
|
|
{
|
|
if( write_to_file == TRUE ) {
|
|
uint_32 idx = CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc;
|
|
#if 0 /* def DEBUG_OUT */
|
|
if ( CurrSeg->e.seginfo->current_loc < CurrSeg->e.seginfo->start_loc )
|
|
_asm int 3;
|
|
#endif
|
|
/**/myassert( CurrSeg->e.seginfo->current_loc >= CurrSeg->e.seginfo->start_loc );
|
|
if( Options.output_format == OFORMAT_OMF && ((idx + len) > MAX_LEDATA_THRESHOLD ) ) {
|
|
omf_FlushCurrSeg();
|
|
idx = CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc;
|
|
}
|
|
if ( fixup )
|
|
store_fixup( fixup, CurrSeg, (int_32 *)pbytes );
|
|
//DebugMsg(("OutputBytes: buff=%p, idx=%" I32_SPEC "X, byte=%X\n", CurrSeg->e.seginfo->CodeBuffer, idx, *pbytes ));
|
|
memcpy( &CurrSeg->e.seginfo->CodeBuffer[idx], pbytes, len );
|
|
}
|
|
#if 1
|
|
/* check this in pass 1 only */
|
|
else if( CurrSeg->e.seginfo->current_loc < CurrSeg->e.seginfo->start_loc ) {
|
|
DebugMsg(("OutputBytes: segment start loc changed from %" I32_SPEC "Xh to %" I32_SPEC "Xh\n",
|
|
CurrSeg->e.seginfo->start_loc,
|
|
CurrSeg->e.seginfo->current_loc));
|
|
CurrSeg->e.seginfo->start_loc = CurrSeg->e.seginfo->current_loc;
|
|
}
|
|
#endif
|
|
CurrSeg->e.seginfo->current_loc += len;
|
|
CurrSeg->e.seginfo->bytes_written += len;
|
|
CurrSeg->e.seginfo->written = TRUE;
|
|
if( CurrSeg->e.seginfo->current_loc > CurrSeg->sym.max_offset )
|
|
CurrSeg->sym.max_offset = CurrSeg->e.seginfo->current_loc;
|
|
}
|
|
|
|
/* set current offset in a segment (usually CurrSeg) without to write anything */
|
|
|
|
ret_code SetCurrOffset( struct dsym *seg, uint_32 value, bool relative, bool select_data )
|
|
/****************************************************************************************/
|
|
{
|
|
if( relative )
|
|
value += seg->e.seginfo->current_loc;
|
|
|
|
if ( Options.output_format == OFORMAT_OMF ) {
|
|
if ( seg == CurrSeg ) {
|
|
if ( write_to_file == TRUE )
|
|
omf_FlushCurrSeg();
|
|
|
|
/* for debugging, tell if data is located in code sections*/
|
|
if( select_data )
|
|
if ( ModuleInfo.CommentDataInCode )
|
|
omf_OutSelect( TRUE );
|
|
LastCodeBufSize = value;
|
|
}
|
|
seg->e.seginfo->start_loc = value;
|
|
/* for -bin, if there's an ORG (relative==false) and no initialized data
|
|
* has been set yet, set start_loc!
|
|
* v1.96: this is now also done for COFF and ELF
|
|
*/
|
|
/* else if ( Options.output_format == OFORMAT_BIN && relative == FALSE ) { */
|
|
} else {
|
|
if ( write_to_file == FALSE ) {
|
|
if ( relative ) {
|
|
#if 0 /* don't include "preceding" uninitialized data */
|
|
if( seg->e.seginfo->current_loc < seg->e.seginfo->start_loc )
|
|
seg->e.seginfo->start_loc = seg->e.seginfo->current_loc;
|
|
#endif
|
|
} else {
|
|
if ( seg->e.seginfo->bytes_written == 0 )
|
|
seg->e.seginfo->start_loc = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
seg->e.seginfo->current_loc = value;
|
|
seg->e.seginfo->written = FALSE;
|
|
|
|
if( seg->e.seginfo->current_loc > seg->sym.max_offset )
|
|
seg->sym.max_offset = seg->e.seginfo->current_loc;
|
|
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* write object module */
|
|
|
|
static ret_code WriteModule( struct module_info *modinfo )
|
|
/********************************************************/
|
|
{
|
|
struct dsym *curr;
|
|
|
|
DebugMsg(("WriteModule enter\n"));
|
|
|
|
/* final checks */
|
|
/* check limit of segments */
|
|
for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
|
|
if ( curr->e.seginfo->Ofssize == USE16 && curr->sym.max_offset > 0x10000 ) {
|
|
if ( Options.output_format == OFORMAT_OMF )
|
|
EmitErr( SEGMENT_EXCEEDS_64K_LIMIT, curr->sym.name );
|
|
else
|
|
EmitWarn( 2, SEGMENT_EXCEEDS_64K_LIMIT, curr->sym.name );
|
|
}
|
|
}
|
|
|
|
modinfo->g.WriteModule( modinfo );
|
|
|
|
#if DLLIMPORT
|
|
/* is the -Fd option given with a file name? */
|
|
if ( Options.names[OPTN_LNKDEF_FN] ) {
|
|
FILE *ld;
|
|
ld = fopen( Options.names[OPTN_LNKDEF_FN], "w" );
|
|
if ( ld == NULL ) {
|
|
return( EmitErr( CANNOT_OPEN_FILE, Options.names[OPTN_LNKDEF_FN], ErrnoStr() ) );
|
|
}
|
|
for ( curr = SymTables[TAB_EXT].head; curr != NULL ; curr = curr->next ) {
|
|
DebugMsg(("WriteModule: ext=%s, isproc=%u, weak=%u\n", curr->sym.name, curr->sym.isproc, curr->sym.weak ));
|
|
if ( curr->sym.isproc && ( curr->sym.weak == FALSE || curr->sym.iat_used ) &&
|
|
curr->sym.dll && *(curr->sym.dll->name) != NULLC ) {
|
|
int size;
|
|
Mangle( &curr->sym, StringBufferEnd );
|
|
size = sprintf( CurrSource, "import '%s' %s.%s\n", StringBufferEnd, curr->sym.dll->name, curr->sym.name );
|
|
if ( fwrite( CurrSource, 1, size, ld ) != size )
|
|
WriteError();
|
|
}
|
|
}
|
|
fclose( ld );
|
|
}
|
|
#endif
|
|
DebugMsg(("WriteModule exit\n"));
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
#define is_valid_first_char( ch ) ( isalpha(ch) || ch=='_' || ch=='@' || ch=='$' || ch=='?' || ch=='.' )
|
|
|
|
/* check name of text macros defined via -D option */
|
|
|
|
static int is_valid_identifier( char *id )
|
|
/****************************************/
|
|
{
|
|
/* special handling of first char of an id: it can't be a digit,
|
|
but can be a dot (don't care about ModuleInfo.dotname!). */
|
|
|
|
if( is_valid_first_char( *id ) == 0 )
|
|
return( ERROR );
|
|
id++;
|
|
for( ; *id != NULLC; id++ ) {
|
|
if ( is_valid_id_char( *id ) == FALSE )
|
|
return( ERROR );
|
|
}
|
|
/* don't allow a single dot! */
|
|
if ( *(id-1) == '.' )
|
|
return( ERROR );
|
|
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* add text macros defined with the -D cmdline switch */
|
|
|
|
static void add_cmdline_tmacros( void )
|
|
/****************************************/
|
|
{
|
|
struct qitem *p;
|
|
char *name;
|
|
char *value;
|
|
int len;
|
|
struct asym *sym;
|
|
|
|
DebugMsg(("add_cmdline_tmacros enter\n"));
|
|
for ( p = Options.queues[OPTQ_MACRO]; p; p = p->next ) {
|
|
DebugMsg(("add_cmdline_tmacros: found >%s<\n", p->value));
|
|
name = p->value;
|
|
value = strchr( name, '=' );
|
|
if( value == NULL ) {
|
|
/* v2.06: ensure that 'value' doesn't point to r/o space */
|
|
//value = "";
|
|
value = name + strlen( name ); /* use the terminating NULL */
|
|
} else {
|
|
len = value - name;
|
|
name = (char *)myalloca( len + 1 );
|
|
memcpy( name, p->value, len );
|
|
*(name + len) = NULLC;
|
|
value++;
|
|
}
|
|
|
|
/* there's no check whether the name is a reserved word!
|
|
*/
|
|
if( is_valid_identifier( name ) == ERROR ) {
|
|
DebugMsg(("add_cmdline_tmacros: name >%s< invalid\n", name ));
|
|
EmitErr( SYNTAX_ERROR_EX, name );
|
|
} else {
|
|
sym = SymSearch( name );
|
|
if ( sym == NULL ) {
|
|
sym = SymCreate( name );
|
|
sym->state = SYM_TMACRO;
|
|
}
|
|
if ( sym->state == SYM_TMACRO ) {
|
|
sym->isdefined = TRUE;
|
|
sym->predefined = TRUE;
|
|
sym->string_ptr = value;
|
|
} else
|
|
EmitErr( SYMBOL_ALREADY_DEFINED, name );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* add the include paths set by -I option */
|
|
|
|
static void add_incpaths( void )
|
|
/******************************/
|
|
{
|
|
struct qitem *p;
|
|
DebugMsg(("add_incpaths: enter\n"));
|
|
for ( p = Options.queues[OPTQ_INCPATH]; p; p = p->next ) {
|
|
AddStringToIncludePath( p->value );
|
|
}
|
|
}
|
|
|
|
/* this is called for every pass.
|
|
* symbol table and ModuleInfo are initialized.
|
|
*/
|
|
static void CmdlParamsInit( int pass )
|
|
/************************************/
|
|
{
|
|
DebugMsg(("CmdlParamsInit(%u) enter\n", pass));
|
|
|
|
#if BUILD_TARGET
|
|
if ( pass == PASS_1 ) {
|
|
struct asym *sym;
|
|
char *tmp;
|
|
char *p;
|
|
|
|
_strupr( Options.build_target );
|
|
tmp = myalloca( strlen( Options.build_target ) + 5 ); /* null + 4 uscores */
|
|
strcpy( tmp, uscores );
|
|
strcat( tmp, Options.build_target );
|
|
strcat( tmp, uscores );
|
|
|
|
/* define target */
|
|
sym = CreateVariable( tmp, 0 );
|
|
sym->predefined = TRUE;
|
|
|
|
p = NULL;
|
|
if( _stricmp( Options.build_target, "DOS" ) == 0 ) {
|
|
p = "__MSDOS__";
|
|
} else if( _stricmp( Options.build_target, "NETWARE" ) == 0 ) {
|
|
if( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
|
|
p = "__NETWARE_386__";
|
|
} else {
|
|
/* do nothing ... __NETWARE__ already defined */
|
|
}
|
|
} else if( _stricmp( Options.build_target, "WINDOWS" ) == 0 ) {
|
|
if( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
|
|
p = "__WINDOWS_386__";
|
|
} else {
|
|
/* do nothing ... __WINDOWS__ already defined */
|
|
}
|
|
} else if( _stricmp( Options.build_target, "QNX" ) == 0 ) {
|
|
p = "__UNIX__";
|
|
} else if( _stricmp( Options.build_target, "LINUX" ) == 0 ) {
|
|
p = "__UNIX__";
|
|
}
|
|
if ( p ) {
|
|
sym = CreateVariable( p, 0 );
|
|
sym->predefined = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( pass == PASS_1 ) {
|
|
char *env;
|
|
/* v2.06: this is done in ModulePassInit now */
|
|
//SetCPU( Options.cpu );
|
|
add_cmdline_tmacros();
|
|
add_incpaths();
|
|
if ( Options.ignore_include == FALSE )
|
|
if ( (env = getenv( "INCLUDE" )) != NULL )
|
|
AddStringToIncludePath( env );
|
|
}
|
|
DebugMsg(("CmdlParamsInit exit\n"));
|
|
return;
|
|
}
|
|
|
|
void WritePreprocessedLine( const char *string )
|
|
/**********************************************/
|
|
/* print out preprocessed source lines
|
|
*/
|
|
{
|
|
static bool PrintEmptyLine = TRUE;
|
|
const char *p;
|
|
|
|
#if 0 /* v2.08: removed, obsolete */
|
|
/* filter some macro specific directives */
|
|
if ( tokenarray[0].token == T_DIRECTIVE &&
|
|
( tokenarray[0].tokval == T_ENDM ||
|
|
tokenarray[0].tokval == T_EXITM))
|
|
return;
|
|
/* don't print generated code - with one exception:
|
|
if the code was generated as a result of structure initialization,
|
|
then do!
|
|
*/
|
|
if ( GeneratedCode )
|
|
return;
|
|
#endif
|
|
if ( Token_Count > 0 ) {
|
|
/* v2.08: don't print a leading % (this char is no longer filtered) */
|
|
for ( p = string; isspace( *p ); p++ );
|
|
printf("%s\n", *p == '%' ? p+1 : string );
|
|
PrintEmptyLine = TRUE;
|
|
} else if ( PrintEmptyLine ) {
|
|
PrintEmptyLine = FALSE;
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
/* set Masm v5.1 compatibility options */
|
|
|
|
void SetMasm510( bool value )
|
|
/***************************/
|
|
{
|
|
ModuleInfo.m510 = value;
|
|
ModuleInfo.oldstructs = value;
|
|
/* ModuleInfo.oldmacros = value; not implemented yet */
|
|
ModuleInfo.dotname = value;
|
|
ModuleInfo.setif2 = value;
|
|
|
|
if ( value ) {
|
|
if ( ModuleInfo.model == MODEL_NONE ) {
|
|
/* if no model is specified, set OFFSET:SEGMENT */
|
|
ModuleInfo.offsettype = OT_SEGMENT;
|
|
if ( ModuleInfo.langtype == LANG_NONE ) {
|
|
ModuleInfo.scoped = FALSE;
|
|
ModuleInfo.procs_private = TRUE;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* called for each pass */
|
|
|
|
static void ModulePassInit( void )
|
|
/********************************/
|
|
{
|
|
enum cpu_info cpu = Options.cpu;
|
|
enum model_type model = Options.model;
|
|
#if DLLIMPORT
|
|
struct dsym *curr;
|
|
#endif
|
|
|
|
DebugMsg(( "ModulePassInit() enter\n" ));
|
|
/* set default values not affected by the masm 5.1 compat switch */
|
|
ModuleInfo.procs_private = FALSE;
|
|
ModuleInfo.procs_export = FALSE;
|
|
ModuleInfo.offsettype = OT_GROUP;
|
|
ModuleInfo.scoped = TRUE;
|
|
|
|
|
|
#if FASTPASS
|
|
/* v2.03: don't generate the code if fastpass is active */
|
|
/* v2.08: query UseSavedState instead of StoreState */
|
|
//if ( StoreState == FALSE ) {
|
|
if ( UseSavedState == FALSE ) {
|
|
#endif
|
|
ModuleInfo.langtype = Options.langtype;
|
|
ModuleInfo.fctype = Options.fctype;
|
|
#if AMD64_SUPPORT
|
|
if ( ModuleInfo.sub_format == SFORMAT_64BIT ) {
|
|
/* v2.06: force cpu to be at least P_64, without side effect to Options.cpu */
|
|
if ( ( cpu & P_CPU_MASK ) < P_64 ) /* enforce cpu to be 64-bit */
|
|
cpu = P_64;
|
|
/* ignore -m switch for 64-bit formats.
|
|
* there's no other model than FLAT possible.
|
|
*/
|
|
model = MODEL_FLAT;
|
|
if ( ModuleInfo.langtype == LANG_NONE && Options.output_format == OFORMAT_COFF )
|
|
ModuleInfo.langtype = LANG_FASTCALL;
|
|
} else
|
|
#endif
|
|
/* if model FLAT is to be set, ensure that cpu is compat. */
|
|
if ( model == MODEL_FLAT && ( cpu & P_CPU_MASK ) < P_386 ) /* cpu < 386? */
|
|
cpu = P_386;
|
|
|
|
SetCPU( cpu );
|
|
/* table ModelToken starts with MODEL_TINY, which is index 1" */
|
|
if ( model != MODEL_NONE )
|
|
AddLineQueueX( "%r %s", T_DOT_MODEL, ModelToken[model - 1] );
|
|
|
|
#if FASTPASS
|
|
}
|
|
#endif
|
|
|
|
SetMasm510( Options.masm51_compat );
|
|
ModuleInfo.defOfssize = USE16;
|
|
ModuleInfo.ljmp = TRUE;
|
|
|
|
ModuleInfo.list = Options.write_listing;
|
|
ModuleInfo.cref = TRUE;
|
|
ModuleInfo.listif = Options.listif;
|
|
ModuleInfo.list_generated_code = Options.list_generated_code;
|
|
ModuleInfo.list_macro = Options.list_macro;
|
|
|
|
ModuleInfo.case_sensitive = Options.case_sensitive;
|
|
ModuleInfo.convert_uppercase = Options.convert_uppercase;
|
|
SymSetCmpFunc();
|
|
|
|
ModuleInfo.segorder = SEGORDER_SEQ;
|
|
ModuleInfo.radix = 10;
|
|
ModuleInfo.fieldalign = Options.fieldalign;
|
|
#if PROCALIGN
|
|
ModuleInfo.procalign = 0;
|
|
#endif
|
|
#if DLLIMPORT
|
|
/* if OPTION DLLIMPORT was used, reset all iat_used flags */
|
|
if ( ModuleInfo.g.DllQueue )
|
|
for ( curr = SymTables[TAB_EXT].head; curr; curr = curr->next )
|
|
curr->sym.iat_used = FALSE;
|
|
#endif
|
|
}
|
|
|
|
#if 0 /* v2.07: removed */
|
|
/* scan - and clear - global queue (EXTERNDEFs).
|
|
* items which have been defined within the module
|
|
* will become public.
|
|
* PROTOs aren't included in the global queue.
|
|
* They will become public when - and if - the PROC directive
|
|
* for the symbol is met.
|
|
*/
|
|
static void scan_globals( void )
|
|
/******************************/
|
|
{
|
|
struct qnode *curr;
|
|
struct qnode *next;
|
|
struct asym *sym;
|
|
|
|
/* turn EXTERNDEFs into PUBLICs if defined in the module.
|
|
* PROCs are handled differently - so ignore these entries here!
|
|
*/
|
|
/* obsolete since v2.07.
|
|
* it's simpler and better to make the symbol public if it turns
|
|
* from SYM_EXTERNAL to SYM_INTERNAL.
|
|
* the other case, that is, the EXTERNDEF comes AFTER the definition,
|
|
* is handled in ExterndefDirective()
|
|
*/
|
|
DebugMsg(("scan_globals: GlobalQueue=%X\n", ModuleInfo.g.GlobalQueue));
|
|
for ( curr = ModuleInfo.g.GlobalQueue.head; curr; curr = next ) {
|
|
next = curr->next;
|
|
sym = (struct asym *)curr->elmt;
|
|
DebugMsg(("scan_globals: %s state=%u used=%u public=%u\n", sym->name, sym->state, sym->used, sym->public ));
|
|
if( sym->state == SYM_INTERNAL && sym->public == FALSE && sym->isproc == FALSE ) {
|
|
/* add it to the public queue */
|
|
sym->public = TRUE;
|
|
QEnqueue( &ModuleInfo.g.PubQueue, curr );
|
|
DebugMsg(("scan_globals: %s added to public queue\n", sym->name ));
|
|
continue; /* don't free this item! */
|
|
}
|
|
LclFree( curr );
|
|
}
|
|
/* the queue is empty now */
|
|
ModuleInfo.g.GlobalQueue.head = NULL;
|
|
}
|
|
#endif
|
|
|
|
/* checks after pass one has been finished without errors */
|
|
|
|
static void PassOneChecks( void )
|
|
/*******************************/
|
|
{
|
|
struct dsym *curr;
|
|
struct dsym *next;
|
|
struct qnode *q;
|
|
struct qnode *qn;
|
|
#ifdef DEBUG_OUT
|
|
int cntUnusedExt = 0;
|
|
#endif
|
|
|
|
/* check for open structures and segments has been done inside the
|
|
* END directive handling already
|
|
* v2.10: now done for PROCs as well, since procedures
|
|
* must be closed BEFORE segments are to be closed.
|
|
*/
|
|
//ProcCheckOpen();
|
|
HllCheckOpen();
|
|
CondCheckOpen();
|
|
|
|
if( ModuleInfo.EndDirFound == FALSE )
|
|
EmitError( END_DIRECTIVE_REQUIRED );
|
|
|
|
#ifdef DEBUG_OUT
|
|
for ( curr = SymTables[TAB_UNDEF].head; curr; curr = curr->next ) {
|
|
DebugMsg(("PassOneChecks: undefined symbol %s\n", curr->sym.name ));
|
|
}
|
|
#endif
|
|
/* v2.04: check the publics queue.
|
|
* - only internal symbols can be public.
|
|
* - weak external symbols are filtered ( since v2.11 )
|
|
* - anything else is an error
|
|
* v2.11: moved here ( from inside the "#if FASTPASS"-block )
|
|
* because the loop will now filter weak externals [ this
|
|
* was previously done in GetPublicSymbols() ]
|
|
*/
|
|
for( q = ModuleInfo.g.PubQueue.head, qn = (struct qnode *)&ModuleInfo.g.PubQueue ; q; q = q->next ) {
|
|
|
|
if ( q->sym->state == SYM_INTERNAL )
|
|
qn = q;
|
|
else if ( q->sym->state == SYM_EXTERNAL && q->sym->weak == TRUE ) {
|
|
DebugMsg(("PassOneChecks: public for weak external skipped: %s\n", q->sym->name ));
|
|
qn->next = q->next;
|
|
LclFree( q );
|
|
q = qn;
|
|
} else {
|
|
DebugMsg(("PassOneChecks: invalid public attribute for %s [state=%u weak=%u]\n", q->sym->name, q->sym->state, q->sym->weak ));
|
|
#if FASTPASS
|
|
SkipSavedState();
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
#if FASTPASS
|
|
if ( SymTables[TAB_UNDEF].head ) {
|
|
/* to force a full second pass in case of missing symbols,
|
|
* activate the next line. It was implemented to have proper
|
|
* error displays if a forward reference wasn't found.
|
|
* However, v1.95 final won't need this anymore, because both
|
|
* filename + lineno for every line is known now in pass 2.
|
|
*/
|
|
/* SkipSavedState(); */
|
|
}
|
|
|
|
/* check if there's an undefined segment reference.
|
|
* This segment was an argument to a group definition then.
|
|
* Just do a full second pass, the GROUP directive will report
|
|
* the error.
|
|
*/
|
|
for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
|
|
if( curr->sym.segment == NULL ) {
|
|
DebugMsg(("PassOneChecks: undefined segment %s\n", curr->sym.name ));
|
|
SkipSavedState();
|
|
break;
|
|
}
|
|
}
|
|
#if COFF_SUPPORT
|
|
/* if there's an item in the safeseh list which is not an
|
|
* internal proc, make a full second pass to emit a proper
|
|
* error msg at the .SAFESEH directive
|
|
*/
|
|
for ( q = ModuleInfo.g.SafeSEHQueue.head; q; q = q->next ) {
|
|
if ( q->sym->state != SYM_INTERNAL || q->sym->isproc == FALSE ) {
|
|
SkipSavedState();
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* scan ALIASes for COFF/ELF */
|
|
|
|
#if COFF_SUPPORT || ELF_SUPPORT
|
|
if ( Options.output_format == OFORMAT_COFF
|
|
#if ELF_SUPPORT
|
|
|| Options.output_format == OFORMAT_ELF
|
|
#endif
|
|
) {
|
|
for( curr = SymTables[TAB_ALIAS].head ; curr != NULL ;curr = curr->next ) {
|
|
struct asym *sym;
|
|
sym = curr->sym.substitute;
|
|
/* check if symbol is external or public */
|
|
if ( sym == NULL ||
|
|
( sym->state != SYM_EXTERNAL &&
|
|
( sym->state != SYM_INTERNAL || sym->ispublic == FALSE ))) {
|
|
SkipSavedState();
|
|
break;
|
|
}
|
|
/* make sure it becomes a strong external */
|
|
if ( sym->state == SYM_EXTERNAL )
|
|
sym->used = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endif /* FASTPASS */
|
|
|
|
/* scan the EXTERN/EXTERNDEF items */
|
|
|
|
for( curr = SymTables[TAB_EXT].head ; curr; curr = next ) {
|
|
next = curr->next;
|
|
/* v2.01: externdefs which have been "used" become "strong" */
|
|
if ( curr->sym.used )
|
|
curr->sym.weak = FALSE;
|
|
/* remove unused EXTERNDEF/PROTO items from queue. */
|
|
if ( curr->sym.weak == TRUE
|
|
#if DLLIMPORT
|
|
&& curr->sym.iat_used == FALSE
|
|
#endif
|
|
) {
|
|
sym_remove_table( &SymTables[TAB_EXT], curr );
|
|
#ifdef DEBUG_OUT
|
|
cntUnusedExt++;
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
#if FASTMEM==0
|
|
/* v2.05: clear fixup list (used for backpatching in pass one) */
|
|
if ( curr->sym.bp_fixup ) {
|
|
struct fixup *c;
|
|
struct fixup *n;
|
|
for( c = curr->sym.bp_fixup ; c; ) {
|
|
n = c->nextbp;
|
|
LclFree( c );
|
|
c = n;
|
|
}
|
|
curr->sym.bp_fixup = NULL;
|
|
}
|
|
#endif
|
|
|
|
if ( curr->sym.iscomm == TRUE )
|
|
continue;
|
|
/* optional alternate symbol must be INTERNAL or EXTERNAL.
|
|
* COFF ( and ELF? ) also wants internal symbols to be public
|
|
* ( which is reasonable, since the linker won't know private
|
|
* symbols and hence will search for a symbol of that name
|
|
* "elsewhere" ).
|
|
*/
|
|
#if FASTPASS
|
|
if ( curr->sym.altname ) {
|
|
if ( curr->sym.altname->state == SYM_INTERNAL ) {
|
|
#if COFF_SUPPORT || ELF_SUPPORT
|
|
/* for COFF/ELF, the altname must be public or external */
|
|
if ( curr->sym.altname->ispublic == FALSE &&
|
|
( Options.output_format == OFORMAT_COFF
|
|
#if ELF_SUPPORT
|
|
|| Options.output_format == OFORMAT_ELF
|
|
#endif
|
|
) ) {
|
|
SkipSavedState();
|
|
}
|
|
#endif
|
|
} else if ( curr->sym.altname->state != SYM_EXTERNAL ) {
|
|
/* do not use saved state, scan full source in second pass */
|
|
SkipSavedState();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef DEBUG_OUT
|
|
DebugMsg(("PassOneChecks: removed unused externals: %u\n", cntUnusedExt ));
|
|
DebugMsg(("PassOneChecks: forward references:\n"));
|
|
for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
|
|
int i;
|
|
int j;
|
|
struct asym * sym;
|
|
struct fixup * fix;
|
|
for ( i = 0, j = 0, sym = curr->e.seginfo->label_list; sym; sym = (struct asym *)((struct dsym *)sym)->next ) {
|
|
i++;
|
|
for ( fix = sym->bp_fixup; fix ; fix = fix->nextbp, j++ );
|
|
}
|
|
DebugMsg(("PassOneChecks: segm=%s, labels=%u forward refs=%u\n", curr->sym.name, i, j));
|
|
}
|
|
#endif
|
|
|
|
if ( ModuleInfo.g.error_count == 0 ) {
|
|
|
|
/* make all symbols of type SYM_INTERNAL, which aren't
|
|
a constant, public. */
|
|
if ( Options.all_symbols_public )
|
|
SymMakeAllSymbolsPublic();
|
|
|
|
if ( Options.syntax_check_only == FALSE )
|
|
write_to_file = TRUE;
|
|
|
|
if ( ModuleInfo.g.Pass1Checks )
|
|
ModuleInfo.g.Pass1Checks( &ModuleInfo );
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
/* do ONE assembly pass
|
|
* the FASTPASS variant (which is default now) doesn't scan the full source
|
|
* for each pass. For this to work, the following things are implemented:
|
|
* 1. in pass one, save state if the first byte is to be emitted.
|
|
* <state> is the segment stack, moduleinfo state, ...
|
|
* 2. once the state is saved, all preprocessed lines must be stored.
|
|
* this can be done here, in OnePass, the line is in <string>.
|
|
* Preprocessed macro lines are stored in RunMacro().
|
|
* 3. for subsequent passes do
|
|
* - restore the state
|
|
* - read preprocessed lines and feed ParseLine() with it
|
|
*/
|
|
static int OnePass( void )
|
|
/************************/
|
|
{
|
|
|
|
InputPassInit();
|
|
ModulePassInit();
|
|
SymPassInit( Parse_Pass );
|
|
LabelInit();
|
|
SegmentInit( Parse_Pass );
|
|
ContextInit( Parse_Pass );
|
|
ProcInit();
|
|
TypesInit();
|
|
HllInit( Parse_Pass );
|
|
MacroInit( Parse_Pass ); /* insert predefined macros */
|
|
AssumeInit( Parse_Pass );
|
|
CmdlParamsInit( Parse_Pass );
|
|
|
|
ModuleInfo.EndDirFound = FALSE;
|
|
ModuleInfo.PhaseError = FALSE;
|
|
//Modend = FALSE;
|
|
/* LineNumber = 0; */
|
|
LinnumInit();
|
|
|
|
#ifdef DEBUG_OUT
|
|
if ( Parse_Pass > PASS_1 ) {
|
|
DebugMsg(("OnePass(%u) segments (current=%s):\n", Parse_Pass + 1, CurrSeg ? CurrSeg->sym.name : "NULL" ));
|
|
{
|
|
struct dsym *dir;
|
|
for( dir = SymTables[TAB_SEG].head; dir; dir = dir->next ) {
|
|
DebugMsg(("OnePass(%u): segm=%-8s typ=%X start=%8X max_ofs=%8X\n", Parse_Pass + 1,
|
|
dir->sym.name, dir->e.seginfo->segtype, dir->e.seginfo->start_loc, dir->sym.max_offset ));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/* the functions above might have written something to the line queue */
|
|
if ( is_linequeue_populated() )
|
|
RunLineQueue();
|
|
#if FASTPASS
|
|
StoreState = FALSE;
|
|
if ( Parse_Pass > PASS_1 && UseSavedState == TRUE ) {
|
|
LineStoreCurr = RestoreState();
|
|
while ( LineStoreCurr && ModuleInfo.EndDirFound == FALSE ) {
|
|
/* the source line is modified in Tokenize() if it contains a comment! */
|
|
#if USELSLINE==0
|
|
strcpy( CurrSource, LineStoreCurr->line );
|
|
#endif
|
|
set_curr_srcfile( LineStoreCurr->srcfile, LineStoreCurr->lineno );
|
|
/* v2.06: list flags now initialized on the top level */
|
|
ModuleInfo.line_flags = 0;
|
|
MacroLevel = ( LineStoreCurr->srcfile == 0xFFF ? 1 : 0 );
|
|
DebugMsg1(("OnePass(%u) cur/nxt=%X/%X src=%X.%u mlvl=%u: >%s<\n", Parse_Pass+1, LineStoreCurr, LineStoreCurr->next, LineStoreCurr->srcfile, LineStoreCurr->lineno, MacroLevel, LineStoreCurr->line ));
|
|
ModuleInfo.CurrComment = NULL; /* v2.08: added (var is never reset because GetTextLine() isn't called) */
|
|
#if USELSLINE
|
|
if ( (Token_Count = Tokenize( LineStoreCurr->line, 0, ModuleInfo.tokenarray, TOK_DEFAULT )) != 0 )
|
|
#else
|
|
if ( (Token_Count = Tokenize( CurrSource, 0, ModuleInfo.tokenarray, TOK_DEFAULT )) != 0 )
|
|
#endif
|
|
ParseLine( ModuleInfo.tokenarray );
|
|
LineStoreCurr = LineStoreCurr->next;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
struct qitem *pq;
|
|
/* v2.11: handle -Fi files here ( previously in CmdlParamsInit ) */
|
|
for ( pq = Options.queues[OPTQ_FINCLUDE]; pq; pq = pq->next ) {
|
|
DebugMsg(("OnePass: force include of file: %s\n", pq->value ));
|
|
if ( SearchFile( pq->value, TRUE ) )
|
|
ProcessFile( ModuleInfo.tokenarray );
|
|
}
|
|
ProcessFile( ModuleInfo.tokenarray ); /* process the main source file */
|
|
}
|
|
|
|
LinnumFini();
|
|
|
|
if ( Parse_Pass == PASS_1 )
|
|
PassOneChecks();
|
|
|
|
ClearSrcStack();
|
|
|
|
return( 1 );
|
|
}
|
|
|
|
#if BUILD_TARGET
|
|
/*
|
|
* from WASM : get os-specific xxx_INCLUDE environment variable.
|
|
* if set, add string to include path.
|
|
*/
|
|
|
|
static void get_os_include( void )
|
|
/********************************/
|
|
{
|
|
char *env;
|
|
char *tmp;
|
|
|
|
/* add OS_include to the include path */
|
|
|
|
tmp = myalloca( strlen( Options.build_target ) + 10 );
|
|
strcpy( tmp, Options.build_target );
|
|
strcat( tmp, "_INCLUDE" );
|
|
|
|
env = getenv( tmp );
|
|
if( env != NULL ) {
|
|
AddStringToIncludePath( env );
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static void get_module_name( void )
|
|
/*********************************/
|
|
{
|
|
//char dummy[_MAX_EXT];
|
|
char *p;
|
|
|
|
/* v2.08: prefer name given by -nm option */
|
|
if ( Options.names[OPTN_MODULE_NAME] ) {
|
|
strncpy( ModuleInfo.name, Options.names[OPTN_MODULE_NAME], sizeof( ModuleInfo.name ) );
|
|
ModuleInfo.name[ sizeof( ModuleInfo.name ) - 1] = NULLC;
|
|
} else {
|
|
/* v2.12: _splitpath()/_makepath() removed */
|
|
const char *fn = GetFNamePart( CurrFName[ASM] );
|
|
char *ext = GetExtPart( fn );
|
|
memcpy( ModuleInfo.name, fn, ext - fn );
|
|
ModuleInfo.name[ ext - fn ] = NULLC;
|
|
//_splitpath( CurrFName[ASM], NULL, NULL, ModuleInfo.name, dummy );
|
|
}
|
|
|
|
_strupr( ModuleInfo.name );
|
|
/* the module name must be a valid identifier, because it's used
|
|
* as part of a segment name in certain memory models.
|
|
*/
|
|
for( p = ModuleInfo.name; *p; ++p ) {
|
|
if( !( isalnum( *p ) || ( *p == '_' ) || ( *p == '$' )
|
|
|| ( *p == '@' ) || ( *p == '?') ) ) {
|
|
/* it's not a legal character for a symbol name */
|
|
*p = '_';
|
|
}
|
|
}
|
|
/* first character can't be a digit either */
|
|
if( isdigit( ModuleInfo.name[0] ) ) {
|
|
ModuleInfo.name[0] = '_';
|
|
}
|
|
}
|
|
|
|
/* called by AssembleInit(), once per source module.
|
|
* symbol table has been initialized here.
|
|
*/
|
|
static void ModuleInit( void )
|
|
/****************************/
|
|
{
|
|
ModuleInfo.sub_format = Options.sub_format;
|
|
ModuleInfo.fmtopt = &formatoptions[Options.output_format];
|
|
ModuleInfo.CommentDataInCode = (Options.output_format == OFORMAT_OMF &&
|
|
Options.no_comment_data_in_code_records == FALSE);
|
|
ModuleInfo.g.error_count = 0;
|
|
ModuleInfo.g.warning_count = 0;
|
|
ModuleInfo.model = MODEL_NONE;
|
|
/* ModuleInfo.distance = STACK_NONE; */
|
|
ModuleInfo.ostype = OPSYS_DOS;
|
|
ModuleInfo.emulator = (Options.floating_point == FPO_EMULATION);
|
|
//ModuleInfo.flatgrp_idx = 0;
|
|
|
|
get_module_name(); /* set ModuleInfo.name */
|
|
|
|
/* v2.06: ST_PROC has been removed */
|
|
//SimpleType[ST_PROC].mem_type = MT_NEAR;
|
|
|
|
memset( SymTables, 0, sizeof( SymTables[0] ) * TAB_LAST );
|
|
ModuleInfo.fmtopt->init( &ModuleInfo );
|
|
|
|
return;
|
|
}
|
|
|
|
static void ReswTableInit( void )
|
|
/*******************************/
|
|
{
|
|
ResWordsInit();
|
|
if ( Options.output_format == OFORMAT_OMF ) {
|
|
/* DebugMsg(("InitAsm: disable IMAGEREL+SECTIONREL\n")); */
|
|
/* for OMF, IMAGEREL and SECTIONREL are disabled */
|
|
#if IMAGERELSUPP
|
|
DisableKeyword( T_IMAGEREL );
|
|
#endif
|
|
#if SECTIONRELSUPP
|
|
DisableKeyword( T_SECTIONREL );
|
|
#endif
|
|
}
|
|
|
|
if ( Options.strict_masm_compat == TRUE ) {
|
|
DebugMsg(("ReswTableInit: disable INCBIN + FASTCALL keywords\n"));
|
|
DisableKeyword( T_INCBIN );
|
|
DisableKeyword( T_FASTCALL );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void open_files( void )
|
|
/****************************/
|
|
{
|
|
/* open ASM file */
|
|
DebugMsg(("open_files() enter\n" ));
|
|
|
|
//memset( CurrFile, 0, sizeof( CurrFile ) );
|
|
/* CurrFile[ASM] = fopen( CurrFName[ASM], "r" ); */
|
|
CurrFile[ASM] = fopen( CurrFName[ASM], "rb" );
|
|
if( CurrFile[ASM] == NULL ) {
|
|
DebugMsg(("open_files(): cannot open source file, fopen(\"%s\") failed\n", CurrFName[ASM] ));
|
|
Fatal( CANNOT_OPEN_FILE, CurrFName[ASM], ErrnoStr() );
|
|
}
|
|
|
|
/* open OBJ file */
|
|
if ( Options.syntax_check_only == FALSE ) {
|
|
CurrFile[OBJ] = fopen( CurrFName[OBJ], "wb" );
|
|
if( CurrFile[OBJ] == NULL ) {
|
|
DebugMsg(("open_files(): cannot open object file, fopen(\"%s\") failed\n", CurrFName[OBJ] ));
|
|
Fatal( CANNOT_OPEN_FILE, CurrFName[OBJ], ErrnoStr() );
|
|
}
|
|
DebugMsg(("open_files(): output, fopen(\"%s\") ok\n", CurrFName[OBJ] ));
|
|
}
|
|
|
|
if( Options.write_listing ) {
|
|
CurrFile[LST] = fopen( CurrFName[LST], "wb" );
|
|
if ( CurrFile[LST] == NULL )
|
|
Fatal( CANNOT_OPEN_FILE, CurrFName[LST], ErrnoStr() );
|
|
}
|
|
return;
|
|
}
|
|
|
|
void close_files( void )
|
|
/**********************/
|
|
{
|
|
/* v2.11: no fatal errors anymore if fclose() fails.
|
|
* That's because Fatal() may cause close_files() to be
|
|
* reentered and thus cause an endless loop.
|
|
*/
|
|
|
|
/* close ASM file */
|
|
if( CurrFile[ASM] != NULL ) {
|
|
if( fclose( CurrFile[ASM] ) != 0 )
|
|
EmitErr( CANNOT_CLOSE_FILE, CurrFName[ASM], errno );
|
|
CurrFile[ASM] = NULL;
|
|
}
|
|
|
|
/* close OBJ file */
|
|
if ( CurrFile[OBJ] != NULL ) {
|
|
if ( fclose( CurrFile[OBJ] ) != 0 )
|
|
EmitErr( CANNOT_CLOSE_FILE, CurrFName[OBJ], errno );
|
|
CurrFile[OBJ] = NULL;
|
|
}
|
|
/* delete the object module if errors occured */
|
|
if ( Options.syntax_check_only == FALSE &&
|
|
ModuleInfo.g.error_count > 0 ) {
|
|
remove( CurrFName[OBJ] );
|
|
}
|
|
|
|
if( CurrFile[LST] != NULL ) {
|
|
fclose( CurrFile[LST] );
|
|
CurrFile[LST] = NULL;
|
|
}
|
|
|
|
/* close ERR file */
|
|
if ( CurrFile[ERR] != NULL ) {
|
|
fclose( CurrFile[ERR] );
|
|
CurrFile[ERR] = NULL;
|
|
} else if ( CurrFName[ERR] )
|
|
/* nothing written, delete any existing ERR file */
|
|
remove( CurrFName[ERR] );
|
|
return;
|
|
}
|
|
|
|
/* get default file extension for error, object and listing files */
|
|
|
|
static char *GetExt( int type )
|
|
/*****************************/
|
|
{
|
|
switch ( type ) {
|
|
case OBJ:
|
|
#if BIN_SUPPORT
|
|
if ( Options.output_format == OFORMAT_BIN )
|
|
#if MZ_SUPPORT || PE_SUPPORT
|
|
if ( Options.sub_format == SFORMAT_MZ
|
|
#if PE_SUPPORT
|
|
|| Options.sub_format == SFORMAT_PE
|
|
#endif
|
|
)
|
|
return( EXE_EXT );
|
|
else
|
|
#endif
|
|
return( BIN_EXT );
|
|
#endif
|
|
return( OBJ_EXT );
|
|
case LST:
|
|
return( LST_EXT );
|
|
case ERR:
|
|
return( ERR_EXT );
|
|
}
|
|
return( NULL );
|
|
}
|
|
|
|
/* set filenames for .obj, .lst and .err
|
|
* in:
|
|
* name: assembly source name
|
|
* DefaultDir[]: default directory part for .obj, .lst and .err
|
|
* in:
|
|
* CurrFName[] for .obj, .lst and .err ( may be NULL )
|
|
* v2.12: _splitpath()/_makepath() removed.
|
|
*/
|
|
|
|
static void SetFilenames( const char *name )
|
|
/******************************************/
|
|
{
|
|
int i;
|
|
const char *fn;
|
|
char *ext;
|
|
char path[ FILENAME_MAX ];
|
|
|
|
DebugMsg(("SetFilenames(\"%s\") enter\n", name ));
|
|
|
|
/* set CurrFName[ASM] */
|
|
CurrFName[ASM] = LclAlloc( strlen( name ) + 1 );
|
|
strcpy( CurrFName[ASM], name );
|
|
|
|
/* set [OBJ], [ERR], [LST] */
|
|
fn = GetFNamePart( name );
|
|
for ( i = ASM+1; i < NUM_FILE_TYPES; i++ ) {
|
|
if( Options.names[i] == NULL ) {
|
|
path[0] = NULLC;
|
|
if ( DefaultDir[i])
|
|
strcpy( path, DefaultDir[i] );
|
|
strcat( path, fn );
|
|
ext = GetExtPart( path );
|
|
*ext++ = '.';
|
|
strcpy( ext, GetExt( i ) );
|
|
|
|
} else {
|
|
/* filename has been set by cmdline option -Fo, -Fl or -Fr */
|
|
const char *fn2;
|
|
strcpy( path, Options.names[i] );
|
|
fn2 = GetFNamePart( path );
|
|
if( *fn2 == NULLC )
|
|
strcpy( (char *)fn2, fn );
|
|
ext = GetExtPart( fn2 );
|
|
if( *ext == NULLC ) {
|
|
*ext++ = '.';
|
|
strcpy( ext, GetExt( i ) );
|
|
}
|
|
}
|
|
DebugMsg(("SetFilenames: i=%u >%s<\n", i, path ));
|
|
CurrFName[i] = LclAlloc( strlen( path ) + 1 );
|
|
strcpy( CurrFName[i], path );
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* init assembler. called once per module */
|
|
|
|
static void AssembleInit( const char *source )
|
|
/********************************************/
|
|
{
|
|
DebugMsg(("AssembleInit(\"%s\") enter\n", source ));
|
|
|
|
MemInit();
|
|
//start_label = NULL;
|
|
//start_displ = 0;
|
|
write_to_file = FALSE;
|
|
//GeneratedCode = 0;
|
|
LinnumQueue.head = NULL;
|
|
|
|
SetFilenames( source );
|
|
|
|
#if FASTPASS
|
|
FastpassInit();
|
|
#endif
|
|
open_files();
|
|
#if BUILD_TARGET
|
|
get_os_include();
|
|
#endif
|
|
ReswTableInit();
|
|
SymInit();
|
|
InputInit();
|
|
|
|
ModuleInit();
|
|
CondInit();
|
|
ExprEvalInit();
|
|
LstInit();
|
|
|
|
DebugMsg(("AssembleInit() exit\n"));
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG_OUT
|
|
void DumpInstrStats( void );
|
|
#endif
|
|
|
|
/* called once per module. AssembleModule() cleanup */
|
|
|
|
static void AssembleFini( void )
|
|
/******************************/
|
|
{
|
|
int i;
|
|
SegmentFini();
|
|
SymFini();
|
|
ResWordsFini();
|
|
#ifdef DEBUG_OUT
|
|
DumpInstrStats();
|
|
MacroFini();
|
|
#endif
|
|
FreePubQueue();
|
|
#if FASTMEM==0
|
|
FreeLibQueue();
|
|
ContextFini();
|
|
HllFini();
|
|
#endif
|
|
InputFini();
|
|
close_files();
|
|
|
|
#if FASTPASS
|
|
#if FASTMEM==0
|
|
FreeLineStore();
|
|
#endif
|
|
#endif
|
|
|
|
for ( i = 0; i < NUM_FILE_TYPES; i++ ) {
|
|
LclFree( CurrFName[i] );
|
|
/* v2.05: make sure the pointer for ERR is cleared */
|
|
CurrFName[i] = NULL;
|
|
}
|
|
MemFini();
|
|
return;
|
|
}
|
|
|
|
/* AssembleModule() assembles one source file */
|
|
|
|
int EXPQUAL AssembleModule( const char *source )
|
|
/**********************************************/
|
|
{
|
|
uint_32 prev_written = -1;
|
|
uint_32 curr_written;
|
|
int starttime;
|
|
int endtime;
|
|
struct dsym *seg;
|
|
|
|
DebugMsg(("AssembleModule(\"%s\") enter\n", source ));
|
|
|
|
memset( &ModuleInfo, 0, sizeof(ModuleInfo) );
|
|
DebugCmd( ModuleInfo.cref = TRUE ); /* enable debug displays */
|
|
|
|
#if 1 //def __SW_BD
|
|
/* fatal errors during assembly won't terminate the program,
|
|
* just the assembly step.!
|
|
*/
|
|
if ( setjmp( jmpenv ) ) {
|
|
if ( ModuleInfo.g.src_stack )
|
|
ClearSrcStack(); /* avoid memory leaks! */
|
|
goto done;
|
|
}
|
|
#endif
|
|
|
|
AssembleInit( source );
|
|
|
|
starttime = clock();
|
|
|
|
#if 0 /* 1=trigger a protection fault */
|
|
seg = NULL;
|
|
seg->sym.state = SYM_UNDEFINED;
|
|
#endif
|
|
|
|
for( Parse_Pass = PASS_1; ; Parse_Pass++ ) {
|
|
|
|
DebugMsg(( "*************\npass %u\n*************\n", Parse_Pass + 1 ));
|
|
OnePass();
|
|
|
|
if( ModuleInfo.g.error_count > 0 ) {
|
|
DebugMsg(("AssembleModule(%u): errorcnt=%u\n", Parse_Pass + 1, ModuleInfo.g.error_count ));
|
|
break;
|
|
}
|
|
|
|
/* calculate total size of segments */
|
|
for ( curr_written = 0, seg = SymTables[TAB_SEG].head; seg ; seg = seg->next ) {
|
|
/* v2.04: use <max_offset> instead of <bytes_written>
|
|
* (the latter is not always reliable due to backpatching).
|
|
*/
|
|
//curr_written += seg->e.seginfo->bytes_written;
|
|
curr_written += seg->sym.max_offset;
|
|
DebugMsg(("AssembleModule(%u): segm=%-8s start=%8" I32_SPEC "X max_ofs=%8" I32_SPEC "X written=%" I32_SPEC "X\n",
|
|
Parse_Pass + 1, seg->sym.name, seg->e.seginfo->start_loc, seg->sym.max_offset,
|
|
seg->e.seginfo->bytes_written ));
|
|
}
|
|
|
|
/* if there's no phase error and size of segments didn't change, we're done */
|
|
DebugMsg(("AssembleModule(%u): PhaseError=%u, prev_written=%" I32_SPEC "X, curr_written=%" I32_SPEC "X\n", Parse_Pass + 1, ModuleInfo.PhaseError, prev_written, curr_written));
|
|
if( !ModuleInfo.PhaseError && prev_written == curr_written )
|
|
break;
|
|
|
|
#ifdef DEBUG_OUT
|
|
if ( curr_written < prev_written && prev_written != -1 ) {
|
|
printf( "size shrank from %" I32_SPEC "X to %" I32_SPEC "X in pass %u\n", prev_written, curr_written, Parse_Pass + 1 );
|
|
}
|
|
#endif
|
|
|
|
DebugMsg(("AssembleModule(%u): prepare for next pass\n", Parse_Pass + 1));
|
|
prev_written = curr_written;
|
|
|
|
if ( Parse_Pass % 200 == 199 )
|
|
EmitWarn( 2, ASSEMBLY_PASSES, Parse_Pass+1 );
|
|
#ifdef DEBUG_OUT
|
|
if ( Options.max_passes && Parse_Pass == (Options.max_passes - 1) )
|
|
break;
|
|
#endif
|
|
if ( Options.line_numbers ) {
|
|
#if COFF_SUPPORT
|
|
if ( Options.output_format == OFORMAT_COFF ) {
|
|
for( seg = SymTables[TAB_SEG].head; seg; seg = seg->next ) {
|
|
if ( seg->e.seginfo->LinnumQueue )
|
|
QueueDeleteLinnum( seg->e.seginfo->LinnumQueue );
|
|
seg->e.seginfo->LinnumQueue = NULL;
|
|
}
|
|
} else {
|
|
#endif
|
|
QueueDeleteLinnum( &LinnumQueue );
|
|
LinnumQueue.head = NULL;
|
|
#if COFF_SUPPORT
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* set file position of ASM and LST files for next pass */
|
|
|
|
rewind( CurrFile[ASM] );
|
|
if ( write_to_file && Options.output_format == OFORMAT_OMF )
|
|
omf_set_filepos();
|
|
|
|
#if FASTPASS
|
|
if ( UseSavedState == FALSE && CurrFile[LST] ) {
|
|
#else
|
|
if ( CurrFile[LST] ) {
|
|
#endif
|
|
rewind( CurrFile[LST] );
|
|
LstInit();
|
|
}
|
|
} /* end for() */
|
|
|
|
if ( ( Parse_Pass > PASS_1 ) && write_to_file )
|
|
WriteModule( &ModuleInfo );
|
|
|
|
if ( ModuleInfo.pCodeBuff ) {
|
|
LclFree( ModuleInfo.pCodeBuff );
|
|
}
|
|
DebugMsg(("AssembleModule: finished, cleanup\n"));
|
|
|
|
/* Write a symbol listing file (if requested) */
|
|
LstWriteCRef();
|
|
|
|
endtime = clock(); /* is in ms already */
|
|
|
|
sprintf( CurrSource, MsgGetEx( MSG_ASSEMBLY_RESULTS ),
|
|
GetFName( ModuleInfo.srcfile )->fname,
|
|
GetLineNumber(),
|
|
Parse_Pass + 1,
|
|
endtime - starttime,
|
|
ModuleInfo.g.warning_count,
|
|
ModuleInfo.g.error_count);
|
|
if ( Options.quiet == FALSE )
|
|
printf( "%s\n", CurrSource );
|
|
|
|
if ( CurrFile[LST] ) {
|
|
LstPrintf( CurrSource );
|
|
LstNL();
|
|
}
|
|
#if 1 //def __SW_BD
|
|
done:
|
|
#endif
|
|
AssembleFini();
|
|
DebugMsg(("AssembleModule exit\n"));
|
|
return( ModuleInfo.g.error_count == 0 );
|
|
}
|