mirror of
https://github.com/NishiOwO/JWasm.git
synced 2025-04-21 16:54:39 +00:00
480 lines
17 KiB
C
480 lines
17 KiB
C
/****************************************************************************
|
|
*
|
|
* This code is Public Domain.
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* Description:
|
|
* function directive
|
|
*--------------------------------------------------
|
|
* EchoDirective() ECHO
|
|
* IncludeDirective() INCLUDE
|
|
* IncludeLibDirective() INCLUDELIB
|
|
* IncBinDirective() INCBIN
|
|
* AliasDirective() ALIAS
|
|
* NameDirective() NAME
|
|
* RadixDirective() .RADIX
|
|
* SegOrderDirective() .DOSSEG, .SEQ, .ALPHA
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "globals.h"
|
|
#include "memalloc.h"
|
|
#include "parser.h"
|
|
#include "segment.h"
|
|
#include "input.h"
|
|
#include "tokenize.h"
|
|
#include "expreval.h"
|
|
#include "types.h"
|
|
#include "fastpass.h"
|
|
#include "listing.h"
|
|
#include "omf.h"
|
|
#include "macro.h"
|
|
|
|
#define res(token, function) extern ret_code function( int, struct asm_tok[] );
|
|
#include "dirtype.h"
|
|
#undef res
|
|
|
|
/* table of function addresses for directives */
|
|
#define res(token, function) function ,
|
|
ret_code (* const directive_tab[])( int, struct asm_tok[] ) = {
|
|
#include "dirtype.h"
|
|
};
|
|
#undef res
|
|
|
|
/* should never be called */
|
|
ret_code StubDir( int i, struct asm_tok tokenarray[] ){ return( ERROR ); }
|
|
|
|
/* handle ECHO directive.
|
|
* displays text on the console
|
|
*/
|
|
ret_code EchoDirective( int i, struct asm_tok tokenarray[] )
|
|
/**********************************************************/
|
|
{
|
|
if ( Parse_Pass == PASS_1 ) /* display in pass 1 only */
|
|
if ( Options.preprocessor_stdout == FALSE ) { /* don't print to stdout if -EP is on! */
|
|
printf( "%s\n", tokenarray[i+1].tokpos );
|
|
}
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* INCLUDE directive.
|
|
* If a full path is specified, the directory where the included file
|
|
* is located becomes the "source" directory, that is, it is searched
|
|
* FIRST if further INCLUDE directives are found inside the included file.
|
|
*/
|
|
ret_code IncludeDirective( int i, struct asm_tok tokenarray[] )
|
|
/*************************************************************/
|
|
{
|
|
char *name;
|
|
|
|
DebugMsg1(("IncludeDirective enter\n"));
|
|
|
|
if ( CurrFile[LST] ) {
|
|
LstWriteSrcLine();
|
|
}
|
|
|
|
i++; /* skip directive */
|
|
/* v2.03: allow plain numbers as file name argument */
|
|
//if ( tokenarray[i].token == T_FINAL || tokenarray[i].token == T_NUM ) {
|
|
if ( tokenarray[i].token == T_FINAL ) {
|
|
return( EmitError( EXPECTED_FILE_NAME ) );
|
|
}
|
|
|
|
/* if the filename is enclosed in <>, just use this literal */
|
|
|
|
if ( tokenarray[i].token == T_STRING && tokenarray[i].string_delim == '<' ) {
|
|
if ( tokenarray[i+1].token != T_FINAL ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i+1].tokpos ) );
|
|
}
|
|
name = tokenarray[i].string_ptr;
|
|
} else {
|
|
char *p;
|
|
/* if the filename isn't enclosed in <>, use anything that comes
|
|
* after INCLUDE - and remove trailing white spaces.
|
|
*/
|
|
name = tokenarray[i].tokpos;
|
|
for ( p = tokenarray[Token_Count].tokpos - 1; p > name && isspace(*p); *p = NULLC, p-- );
|
|
}
|
|
if ( SearchFile( name, TRUE ) )
|
|
ProcessFile( tokenarray ); /* v2.11: process the file synchronously */
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
static char *IncludeLibrary( const char *name )
|
|
/*********************************************/
|
|
{
|
|
struct qitem *q;
|
|
|
|
/* old approach, <= 1.91: add lib name to global namespace */
|
|
/* new approach, >= 1.92: check lib table, if entry is missing, add it */
|
|
/* Masm doesn't map cases for the paths. So if there is
|
|
* includelib <kernel32.lib>
|
|
* includelib <KERNEL32.LIB>
|
|
* then 2 defaultlib entries are added. If this is to be changed for
|
|
* JWasm, activate the _stricmp() below.
|
|
*/
|
|
for ( q = ModuleInfo.g.LibQueue.head; q ; q = q->next ) {
|
|
//if ( _stricmp( dir->sym.name, name) == 0)
|
|
if ( strcmp( q->value, name ) == 0 )
|
|
return( q->value );
|
|
}
|
|
q = LclAlloc( sizeof( struct qitem ) + strlen( name ) );
|
|
strcpy( q->value, name );
|
|
QEnqueue( &ModuleInfo.g.LibQueue, q );
|
|
return( q->value );
|
|
}
|
|
|
|
#if FASTMEM==0
|
|
|
|
/* release the lib queue ( created by INCLUDELIB directive ) */
|
|
|
|
void FreeLibQueue( void )
|
|
/***********************/
|
|
{
|
|
struct qitem *curr;
|
|
struct qitem *next;
|
|
for( curr = ModuleInfo.g.LibQueue.head; curr; curr = next ) {
|
|
next = curr->next;
|
|
LclFree( curr );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* directive INCLUDELIB */
|
|
|
|
ret_code IncludeLibDirective( int i, struct asm_tok tokenarray[] )
|
|
/****************************************************************/
|
|
{
|
|
char *name;
|
|
//struct asym *sym;
|
|
|
|
if ( Parse_Pass != PASS_1 ) /* do all work in pass 1 */
|
|
return( NOT_ERROR );
|
|
i++; /* skip the directive */
|
|
/* v2.03: library name may be just a "number" */
|
|
//if ( tokenarray[i].token == T_FINAL || tokenarray[i].token == T_NUM ) {
|
|
if ( tokenarray[i].token == T_FINAL ) {
|
|
/* v2.05: Masm doesn't complain if there's no name, so emit a warning only! */
|
|
//EmitError( LIBRARY_NAME_MISSING );
|
|
//return( ERROR );
|
|
EmitWarn( 2, LIBRARY_NAME_MISSING );
|
|
}
|
|
|
|
if ( tokenarray[i].token == T_STRING && tokenarray[i].string_delim == '<' ) {
|
|
if ( tokenarray[i+1].token != T_FINAL ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i+1].tokpos ) );
|
|
}
|
|
/* v2.08: use GetLiteralValue() */
|
|
//name = StringBufferEnd;
|
|
//GetLiteralValue( name, tokenarray[i].string_ptr );
|
|
name = tokenarray[i].string_ptr;
|
|
} else {
|
|
char *p;
|
|
/* regard "everything" behind INCLUDELIB as the library name */
|
|
name = tokenarray[i].tokpos;
|
|
/* remove trailing white spaces */
|
|
for ( p = tokenarray[Token_Count].tokpos - 1; p > name && isspace( *p ); *p = NULLC, p-- );
|
|
}
|
|
|
|
IncludeLibrary( name );
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
#if INCBINSUPP
|
|
|
|
/* INCBIN directive */
|
|
|
|
ret_code IncBinDirective( int i, struct asm_tok tokenarray[] )
|
|
/************************************************************/
|
|
{
|
|
FILE *file;
|
|
//int size;
|
|
uint_32 fileoffset = 0; /* fixme: should be uint_64 */
|
|
uint_32 sizemax = -1;
|
|
struct expr opndx;
|
|
|
|
DebugMsg(("IncBinDirective enter\n"));
|
|
|
|
i++; /* skip the directive */
|
|
/* v2.03: file name may be just a "number" */
|
|
//if ( tokenarray[i].token == T_FINAL || tokenarray[i].token == T_NUM ) {
|
|
if ( tokenarray[i].token == T_FINAL ) {
|
|
return( EmitError( EXPECTED_FILE_NAME ) );
|
|
}
|
|
|
|
if ( tokenarray[i].token == T_STRING ) {
|
|
|
|
/* v2.08: use string buffer to avoid buffer overflow if string is > FILENAME_MAX */
|
|
if ( tokenarray[i].string_delim == '"' || tokenarray[i].string_delim == '\'' ) {
|
|
memcpy( StringBufferEnd, tokenarray[i].string_ptr+1, tokenarray[i].stringlen );
|
|
StringBufferEnd[tokenarray[i].stringlen] = NULLC;
|
|
} else if ( tokenarray[i].string_delim == '<' ) {
|
|
/* v2.08: use GetLiteralValue() instead of strncpy() */
|
|
//GetLiteralValue( StringBufferEnd, tokenarray[i].string_ptr );
|
|
memcpy( StringBufferEnd, tokenarray[i].string_ptr, tokenarray[i].stringlen+1 );
|
|
} else {
|
|
return( EmitError( FILENAME_MUST_BE_ENCLOSED_IN_QUOTES_OR_BRACKETS ) );
|
|
}
|
|
} else {
|
|
return( EmitError( FILENAME_MUST_BE_ENCLOSED_IN_QUOTES_OR_BRACKETS ) );
|
|
}
|
|
i++;
|
|
if ( tokenarray[i].token == T_COMMA ) {
|
|
i++;
|
|
if ( EvalOperand( &i, tokenarray, Token_Count, &opndx, 0 ) == ERROR )
|
|
return( ERROR );
|
|
if ( opndx.kind == EXPR_CONST ) {
|
|
fileoffset = opndx.value;
|
|
} else if ( opndx.kind != EXPR_EMPTY ) {
|
|
return( EmitError( CONSTANT_EXPECTED ) );
|
|
}
|
|
if ( tokenarray[i].token == T_COMMA ) {
|
|
i++;
|
|
if ( EvalOperand( &i, tokenarray, Token_Count, &opndx, 0 ) == ERROR )
|
|
return( ERROR );
|
|
if ( opndx.kind == EXPR_CONST ) {
|
|
sizemax = opndx.value;
|
|
} else if ( opndx.kind != EXPR_EMPTY ) {
|
|
return( EmitError( CONSTANT_EXPECTED ) );
|
|
}
|
|
}
|
|
}
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) );
|
|
}
|
|
|
|
if( CurrSeg == NULL ) {
|
|
return( EmitError( MUST_BE_IN_SEGMENT_BLOCK ) );
|
|
}
|
|
|
|
/* v2.04: tell assembler that data is emitted */
|
|
if ( ModuleInfo.CommentDataInCode )
|
|
omf_OutSelect( TRUE );
|
|
|
|
DebugMsg1(("IncBinDirective: filename=%s, offset=%" I32_SPEC "u, size=%" I32_SPEC "u\n", StringBufferEnd, fileoffset, sizemax ));
|
|
|
|
/* try to open the file */
|
|
if ( file = SearchFile( StringBufferEnd, FALSE ) ) {
|
|
/* transfer file content to the current segment. */
|
|
if ( fileoffset )
|
|
fseek( file, fileoffset, SEEK_SET ); /* fixme: use fseek64() */
|
|
for( ; sizemax; sizemax-- ) {
|
|
int ch = fgetc( file );
|
|
if ( ( ch == EOF ) && feof( file ) )
|
|
break;
|
|
OutputByte( ch );
|
|
}
|
|
fclose( file );
|
|
}
|
|
|
|
return( NOT_ERROR );
|
|
}
|
|
#endif
|
|
|
|
/* Alias directive.
|
|
* Masm syntax is:
|
|
* 'ALIAS <alias_name> = <substitute_name>'
|
|
* which looks somewhat strange if compared to other Masm directives.
|
|
* (OW Wasm syntax is 'alias_name ALIAS substitute_name', which is
|
|
* what one might have expected for Masm as well).
|
|
*
|
|
* <alias_name> is a global name and must be unique (that is, NOT be
|
|
* defined elsewhere in the source!
|
|
* <substitute_name> is the name which is defined in the source.
|
|
* For COFF and ELF, this name MUST be defined somewhere as
|
|
* external or public!
|
|
*/
|
|
|
|
ret_code AliasDirective( int i, struct asm_tok tokenarray[] )
|
|
/***********************************************************/
|
|
{
|
|
//char *tmp;
|
|
struct asym *sym;
|
|
char *subst;
|
|
|
|
i++; /* go past ALIAS */
|
|
|
|
if ( tokenarray[i].token != T_STRING ||
|
|
tokenarray[i].string_delim != '<' ) {
|
|
DebugMsg(("AliasDirective: first argument is not a literal: %s\n", tokenarray[i].string_ptr ));
|
|
return( EmitError( TEXT_ITEM_REQUIRED ) );
|
|
}
|
|
|
|
/* check syntax. note that '=' is T_DIRECTIVE && DRT_EQUALSGN */
|
|
if ( tokenarray[i+1].token != T_DIRECTIVE ||
|
|
//tokenarray[i+1].tokval != T_EQU ||
|
|
tokenarray[i+1].dirtype != DRT_EQUALSGN ) {
|
|
DebugMsg(("AliasDirective: syntax error: %s\n", tokenarray[i+1].string_ptr ));
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i+1].string_ptr ) );
|
|
}
|
|
|
|
if ( tokenarray[i+2].token != T_STRING ||
|
|
tokenarray[i+2].string_delim != '<' ) {
|
|
DebugMsg(("AliasDirective: second argument is not a literal: %s\n", tokenarray[i+2].string_ptr ));
|
|
return( EmitError( TEXT_ITEM_REQUIRED ) );
|
|
}
|
|
subst = tokenarray[i+2].string_ptr;
|
|
|
|
if ( tokenarray[i+3].token != T_FINAL ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i+3].string_ptr ) );
|
|
}
|
|
|
|
/* make sure <alias_name> isn't defined elsewhere */
|
|
sym = SymSearch( tokenarray[i].string_ptr );
|
|
if ( sym == NULL || sym->state == SYM_UNDEFINED ) {
|
|
struct asym *sym2;
|
|
/* v2.04b: adjusted to new field <substitute> */
|
|
sym2 = SymSearch( subst );
|
|
if ( sym2 == NULL ) {
|
|
sym2 = SymCreate( subst );
|
|
sym2->state = SYM_UNDEFINED;
|
|
sym_add_table( &SymTables[TAB_UNDEF], (struct dsym *)sym2 );
|
|
} else if ( sym2->state != SYM_UNDEFINED &&
|
|
sym2->state != SYM_INTERNAL &&
|
|
sym2->state != SYM_EXTERNAL ) {
|
|
return( EmitErr( MUST_BE_PUBLIC_OR_EXTERNAL, subst ) );
|
|
}
|
|
if ( sym == NULL )
|
|
sym = SymCreate( tokenarray[i].string_ptr );
|
|
else
|
|
sym_remove_table( &SymTables[TAB_UNDEF], (struct dsym *)sym );
|
|
|
|
sym->state = SYM_ALIAS;
|
|
sym->substitute = sym2;
|
|
/* v2.10: copy language type of alias */
|
|
sym->langtype = sym2->langtype;
|
|
sym_add_table( &SymTables[TAB_ALIAS], (struct dsym *)sym ); /* add ALIAS */
|
|
return( NOT_ERROR );
|
|
}
|
|
if ( sym->state != SYM_ALIAS || ( strcmp( sym->substitute->name, subst ) != 0 )) {
|
|
DebugMsg(("AliasDirective: symbol redefinition\n"));
|
|
return( EmitErr( SYMBOL_REDEFINITION, sym->name ) );
|
|
}
|
|
#if COFF_SUPPORT || ELF_SUPPORT
|
|
/* for COFF+ELF, make sure <actual_name> is "global" (EXTERNAL or
|
|
* public INTERNAL). For OMF, there's no check at all. */
|
|
if ( Parse_Pass != PASS_1 ) {
|
|
if ( Options.output_format == OFORMAT_COFF
|
|
#if ELF_SUPPORT
|
|
|| Options.output_format == OFORMAT_ELF
|
|
#endif
|
|
) {
|
|
if ( sym->substitute->state == SYM_UNDEFINED ) {
|
|
return( EmitErr( SYMBOL_NOT_DEFINED, subst ) );
|
|
} else if ( sym->substitute->state != SYM_EXTERNAL &&
|
|
( sym->substitute->state != SYM_INTERNAL || sym->substitute->ispublic == FALSE ) ) {
|
|
return( EmitErr( MUST_BE_PUBLIC_OR_EXTERNAL, subst ) );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* the NAME directive is ignored in Masm v6 */
|
|
|
|
ret_code NameDirective( int i, struct asm_tok tokenarray[] )
|
|
/**********************************************************/
|
|
{
|
|
if( Parse_Pass != PASS_1 )
|
|
return( NOT_ERROR );
|
|
/* if a module name is set with -nm, ignore NAME directive! */
|
|
/* v2.08: removed, since Options.names isn't touched at all */
|
|
//if( Options.names[OPTN_MODULE_NAME] != NULL )
|
|
// return( NOT_ERROR );
|
|
|
|
i++; /* skip directive */
|
|
|
|
/* improper use of NAME is difficult to see since it is a nop
|
|
therefore some syntax checks are implemented:
|
|
- no 'name' structs, unions, records, typedefs!
|
|
- no 'name' struct fields!
|
|
- no 'name' segments!
|
|
- no 'name:' label!
|
|
*/
|
|
if ( CurrStruct != NULL ||
|
|
( tokenarray[i].token == T_DIRECTIVE &&
|
|
( tokenarray[i].tokval == T_SEGMENT ||
|
|
tokenarray[i].tokval == T_STRUCT ||
|
|
tokenarray[i].tokval == T_STRUC ||
|
|
tokenarray[i].tokval == T_UNION ||
|
|
tokenarray[i].tokval == T_TYPEDEF ||
|
|
tokenarray[i].tokval == T_RECORD)) ||
|
|
tokenarray[i].token == T_COLON ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i-1].tokpos ) );
|
|
}
|
|
|
|
/* don't touch Option fields! if anything at all, ModuleInfo.name may be modified.
|
|
* However, since the directive is ignored by Masm, nothing is done.
|
|
*/
|
|
// strncpy( ModuleInfo.name, tokenarray[i].string_ptr, sizeof( ModuleInfo.name ) );
|
|
// ModuleInfo.name[ sizeof( ModuleInfo.name ) - 1] = NULLC;
|
|
// DebugMsg(("NameDirective: set name to >%s<\n", ModuleInfo.name ));
|
|
DebugMsg(("NameDirective: ignored name >%s<\n", tokenarray[i].string_ptr ));
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* .RADIX directive, value must be between 2 .. 16 */
|
|
|
|
ret_code RadixDirective( int i, struct asm_tok tokenarray[] )
|
|
/***********************************************************/
|
|
{
|
|
uint_8 oldradix;
|
|
struct expr opndx;
|
|
|
|
/* to get the .radix parameter, enforce radix 10 and retokenize! */
|
|
oldradix = ModuleInfo.radix;
|
|
ModuleInfo.radix = 10;
|
|
i++; /* skip directive token */
|
|
Tokenize( tokenarray[i].tokpos, i, tokenarray, TOK_RESCAN );
|
|
ModuleInfo.radix = oldradix;
|
|
/* v2.11: flag NOUNDEF added - no forward ref possible */
|
|
if ( EvalOperand( &i, tokenarray, Token_Count, &opndx, EXPF_NOUNDEF ) == ERROR ) {
|
|
return( ERROR );
|
|
}
|
|
|
|
if ( opndx.kind != EXPR_CONST ) {
|
|
return( EmitError( CONSTANT_EXPECTED ) );
|
|
}
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) );
|
|
}
|
|
if ( opndx.value > 16 || opndx.value < 2 || opndx.hvalue != 0 ) {
|
|
return( EmitError( INVALID_RADIX_TAG ) );
|
|
}
|
|
|
|
ModuleInfo.radix = opndx.value;
|
|
DebugMsg(("RadixDirective: new radix=%u\n", ModuleInfo.radix ));
|
|
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* DOSSEG, .DOSSEG, .ALPHA, .SEQ directives */
|
|
|
|
ret_code SegOrderDirective( int i, struct asm_tok tokenarray[] )
|
|
/**************************************************************/
|
|
{
|
|
if ( tokenarray[i+1].token != T_FINAL ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i+1].tokpos ) );
|
|
}
|
|
#if COFF_SUPPORT || ELF_SUPPORT || PE_SUPPORT
|
|
if ( Options.output_format == OFORMAT_COFF
|
|
#if ELF_SUPPORT
|
|
|| Options.output_format == OFORMAT_ELF
|
|
#endif
|
|
#if PE_SUPPORT
|
|
|| ( Options.output_format == OFORMAT_BIN && ModuleInfo.sub_format == SFORMAT_PE )
|
|
#endif
|
|
) {
|
|
if ( Parse_Pass == PASS_1 )
|
|
EmitWarn( 2, NOT_SUPPORTED_WITH_CURR_FORMAT, _strupr( tokenarray[i].string_ptr ) );
|
|
} else
|
|
#endif
|
|
ModuleInfo.segorder = GetSflagsSp( tokenarray[i].tokval );
|
|
|
|
return( NOT_ERROR );
|
|
}
|