1 /****************************************************************************
2 *
3 *  This code is Public Domain.
4 *
5 *  ========================================================================
6 *
7 * Description:  assemble a module.
8 *
9 ****************************************************************************/
10 
11 #include <ctype.h>
12 #include <time.h>
13 
14 #include "globals.h"
15 #include "memalloc.h"
16 #include "input.h"
17 #include "parser.h"
18 #include "reswords.h"
19 #include "tokenize.h"
20 #include "condasm.h"
21 #include "segment.h"
22 #include "assume.h"
23 #include "proc.h"
24 #include "expreval.h"
25 #include "hll.h"
26 #include "context.h"
27 #include "types.h"
28 #include "label.h"
29 #include "macro.h"
30 #include "extern.h"
31 #include "fixup.h"
32 #include "omf.h"
33 #include "fastpass.h"
34 #include "listing.h"
35 #include "msgtext.h"
36 #include "myassert.h"
37 #include "linnum.h"
38 #include "cpumodel.h"
39 #include "lqueue.h"
40 #if DLLIMPORT
41 #include "mangle.h"
42 #endif
43 
44 #if COFF_SUPPORT
45 #include "coff.h"
46 #endif
47 #if ELF_SUPPORT
48 #include "elf.h"
49 #endif
50 #if BIN_SUPPORT
51 #include "bin.h"
52 #endif
53 
54 #if 1 //def __SW_BD
55 #include <setjmp.h>
56 jmp_buf jmpenv;
57 #endif
58 
59 #ifdef __SW_BD
60 #define EXPQUAL __stdcall
61 #else
62 #define EXPQUAL
63 #endif
64 
65 #define USELSLINE 1 /* must match switch in listing.c! */
66 
67 //#define ASM_EXT "asm"
68 #ifdef __UNIX__
69 #define OBJ_EXT "o"
70 #else
71 #define OBJ_EXT "obj"
72 #endif
73 #define LST_EXT "lst"
74 #define ERR_EXT "err"
75 #define BIN_EXT "BIN"
76 #define EXE_EXT "EXE"
77 
78 extern int_32           LastCodeBufSize;
79 extern char             *DefaultDir[NUM_FILE_TYPES];
80 extern const char       *ModelToken[];
81 #if FASTMEM==0
82 extern void             FreeLibQueue();
83 #endif
84 
85 /* parameters for output formats. order must match enum oformat */
86 static const struct format_options formatoptions[] = {
87 #if BIN_SUPPORT
88     { bin_init,  BIN_DISALLOWED,    "BIN"  },
89 #endif
90     { omf_init,  OMF_DISALLOWED,    "OMF"  },
91 #if COFF_SUPPORT
92     { coff_init, COFF32_DISALLOWED, "COFF" },
93 #endif
94 #if ELF_SUPPORT
95     { elf_init,  ELF32_DISALLOWED,  "ELF"  },
96 #endif
97 };
98 
99 struct module_info      ModuleInfo;
100 unsigned int            Parse_Pass;     /* assembly pass */
101 //unsigned int            GeneratedCode; /* v2.10: moved to ModuleInfo */
102 struct qdesc            LinnumQueue;    /* queue of line_num_info items */
103 
104 bool write_to_file;     /* write object module */
105 
106 
107 #if 0
108 /* for OW, it would be good to remove the CharUpperA() emulation
109  * implemented in apiemu.c. Unfortunately, OW isn't happy with
110  * a local, simple version of _strupr() - it still wants to
111  * import CharUpperA.
112  */
113 char * _strupr( char *src )
114 {
115     char *dst;
116     for ( dst = src; *dst; dst++ )
117         if ( *dst >= 'a' && *dst <= 'z' )
118             *dst &= ~0x20;
119     return( src );
120 }
121 #endif
122 
123 #if COFF_SUPPORT || PE_SUPPORT
124 
125 /* struct to help convert section names in COFF, ELF, PE */
126 struct conv_section {
127     uint_8 len;
128     uint_8 flags; /* see below */
129     const char *src;
130     const char *dst;
131 };
132 enum cvs_flags {
133     CSF_GRPCHK = 1
134 };
135 
136 enum conv_section_index {
137     CSI_TEXT = 0,
138     CSI_DATA,
139     CSI_CONST,
140     CSI_BSS
141 };
142 
143 /* order must match enum conv_section_index above */
144 static const struct conv_section cst[] = {
145     { 5, CSF_GRPCHK, "_TEXT", ".text"  },
146     { 5, CSF_GRPCHK, "_DATA", ".data"  },
147     { 5, CSF_GRPCHK, "CONST", ".rdata" },
148     { 4, 0,          "_BSS",  ".bss"   }
149 };
150 
151 /* order must match enum conv_section_index above */
152 static const enum seg_type stt[] = {
153     SEGTYPE_CODE, SEGTYPE_DATA, SEGTYPE_DATA, SEGTYPE_BSS
154 };
155 
156 /*
157  * translate section names (COFF+PE):
158  * _TEXT -> .text
159  * _DATA -> .data
160  * CONST -> .rdata
161  * _BSS  -> .bss
162  */
163 
ConvertSectionName(const struct asym * sym,enum seg_type * pst,char * buffer)164 char *ConvertSectionName( const struct asym *sym, enum seg_type *pst, char *buffer )
165 /**********************************************************************************/
166 {
167     int i;
168 
169     for ( i = 0; i < sizeof( cst ) / sizeof( cst[0] ); i++ ) {
170         if ( memcmp( sym->name, cst[i].src, cst[i].len ) == 0 ) {
171             if ( sym->name[cst[i].len] == NULLC || ( sym->name[cst[i].len] == '$' && ( cst[i].flags & CSF_GRPCHK ) ) ) {
172 
173                 if ( pst ) {
174                     if ( i == CSI_BSS && ( (struct dsym *)sym)->e.seginfo->bytes_written != 0 )
175                         ; /* don't set segment type to BSS if the segment contains initialized data */
176                     else
177                         *pst = stt[i];
178                 }
179 
180                 if ( sym->name[cst[i].len] == NULLC ) {
181 #if DJGPP_SUPPORT
182                     /* DJGPP won't be happy with .rdata segment name */
183                     if( ModuleInfo.sub_format == SFORMAT_DJGPP && i == CSI_CONST )
184                         return( ".const" );
185 #endif
186                     return( (char *)cst[i].dst );
187                 }
188 
189                 strcpy( buffer, cst[i].dst );
190                 strcat( buffer, sym->name+cst[i].len );
191                 return( buffer );
192             }
193         }
194     }
195     return( sym->name );
196 }
197 #endif
198 
199 /* Write a byte to the segment buffer.
200  * in OMF, the segment buffer is flushed when the max. record size is reached.
201  */
OutputByte(unsigned char byte)202 void OutputByte( unsigned char byte )
203 /***********************************/
204 {
205     if( write_to_file == TRUE ) {
206         uint_32 idx = CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc;
207 #ifdef DEBUG_OUT
208         if ( CurrSeg->e.seginfo->current_loc < CurrSeg->e.seginfo->start_loc ) {
209             //_asm int 3;
210         }
211 #endif
212         /**/myassert( CurrSeg->e.seginfo->current_loc >= CurrSeg->e.seginfo->start_loc );
213         if( Options.output_format == OFORMAT_OMF && idx >= MAX_LEDATA_THRESHOLD ) {
214             omf_FlushCurrSeg();
215             idx = CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc;
216         }
217         //DebugMsg(("OutputByte: buff=%p, idx=%" I32_SPEC "X, byte=%X, codebuff[0]=%X\n", CurrSeg->e.seginfo->CodeBuffer, idx, byte, *CurrSeg->e.seginfo->CodeBuffer ));
218         CurrSeg->e.seginfo->CodeBuffer[idx] = byte;
219     }
220 #if 1
221     /* check this in pass 1 only */
222     else if( CurrSeg->e.seginfo->current_loc < CurrSeg->e.seginfo->start_loc ) {
223         DebugMsg(("OutputByte: segment start loc changed from %" I32_SPEC "Xh to %" I32_SPEC "Xh\n",
224                   CurrSeg->e.seginfo->start_loc,
225                   CurrSeg->e.seginfo->current_loc));
226         CurrSeg->e.seginfo->start_loc = CurrSeg->e.seginfo->current_loc;
227     }
228 #endif
229     CurrSeg->e.seginfo->current_loc++;
230     CurrSeg->e.seginfo->bytes_written++;
231     CurrSeg->e.seginfo->written = TRUE;
232     if( CurrSeg->e.seginfo->current_loc > CurrSeg->sym.max_offset )
233         CurrSeg->sym.max_offset = CurrSeg->e.seginfo->current_loc;
234 }
235 
236 #if 0 /* v2.03: OutputCodeByte is obsolete */
237 void OutputCodeByte( unsigned char byte )
238 /***************************************/
239 {
240     // if ( ModuleInfo.CommentDataInCode )
241     // omf_OutSelect( FALSE );
242     OutputByte( byte );
243 }
244 #endif
245 
FillDataBytes(unsigned char byte,int len)246 void FillDataBytes( unsigned char byte, int len )
247 /***********************************************/
248 {
249     if ( ModuleInfo.CommentDataInCode )
250         omf_OutSelect( TRUE );
251     for( ; len; len-- )
252         OutputByte( byte );
253 }
254 
255 /*
256  * this function is to output (small, <= 8) amounts of bytes which must
257  * not be separated ( for omf, because of fixups )
258  */
259 
OutputBytes(const unsigned char * pbytes,int len,struct fixup * fixup)260 void OutputBytes( const unsigned char *pbytes, int len, struct fixup *fixup )
261 /***************************************************************************/
262 {
263     if( write_to_file == TRUE ) {
264         uint_32 idx = CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc;
265 #if 0 /* def DEBUG_OUT */
266         if ( CurrSeg->e.seginfo->current_loc < CurrSeg->e.seginfo->start_loc )
267             _asm int 3;
268 #endif
269         /**/myassert( CurrSeg->e.seginfo->current_loc >= CurrSeg->e.seginfo->start_loc );
270         if( Options.output_format == OFORMAT_OMF && ((idx + len) > MAX_LEDATA_THRESHOLD ) ) {
271             omf_FlushCurrSeg();
272             idx = CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc;
273         }
274         if ( fixup )
275             store_fixup( fixup, CurrSeg, (int_32 *)pbytes );
276         //DebugMsg(("OutputBytes: buff=%p, idx=%" I32_SPEC "X, byte=%X\n", CurrSeg->e.seginfo->CodeBuffer, idx, *pbytes ));
277         memcpy( &CurrSeg->e.seginfo->CodeBuffer[idx], pbytes, len );
278     }
279 #if 1
280     /* check this in pass 1 only */
281     else if( CurrSeg->e.seginfo->current_loc < CurrSeg->e.seginfo->start_loc ) {
282         DebugMsg(("OutputBytes: segment start loc changed from %" I32_SPEC "Xh to %" I32_SPEC "Xh\n",
283                   CurrSeg->e.seginfo->start_loc,
284                   CurrSeg->e.seginfo->current_loc));
285         CurrSeg->e.seginfo->start_loc = CurrSeg->e.seginfo->current_loc;
286     }
287 #endif
288     CurrSeg->e.seginfo->current_loc += len;
289     CurrSeg->e.seginfo->bytes_written += len;
290     CurrSeg->e.seginfo->written = TRUE;
291     if( CurrSeg->e.seginfo->current_loc > CurrSeg->sym.max_offset )
292         CurrSeg->sym.max_offset = CurrSeg->e.seginfo->current_loc;
293 }
294 
295 /* set current offset in a segment (usually CurrSeg) without to write anything */
296 
SetCurrOffset(struct dsym * seg,uint_32 value,bool relative,bool select_data)297 ret_code SetCurrOffset( struct dsym *seg, uint_32 value, bool relative, bool select_data )
298 /****************************************************************************************/
299 {
300     if( relative )
301         value += seg->e.seginfo->current_loc;
302 
303     if ( Options.output_format == OFORMAT_OMF ) {
304         if ( seg == CurrSeg ) {
305             if ( write_to_file == TRUE )
306                 omf_FlushCurrSeg();
307 
308         /* for debugging, tell if data is located in code sections*/
309             if( select_data )
310                 if ( ModuleInfo.CommentDataInCode )
311                     omf_OutSelect( TRUE );
312             LastCodeBufSize = value;
313         }
314         seg->e.seginfo->start_loc = value;
315     /* for -bin, if there's an ORG (relative==false) and no initialized data
316      * has been set yet, set start_loc!
317      * v1.96: this is now also done for COFF and ELF
318      */
319     /* else if ( Options.output_format == OFORMAT_BIN && relative == FALSE ) { */
320     } else {
321         if ( write_to_file == FALSE ) {
322             if ( relative ) {
323 #if 0 /* don't include "preceding" uninitialized data */
324                 if( seg->e.seginfo->current_loc < seg->e.seginfo->start_loc )
325                     seg->e.seginfo->start_loc = seg->e.seginfo->current_loc;
326 #endif
327             } else {
328                 if ( seg->e.seginfo->bytes_written == 0 )
329                     seg->e.seginfo->start_loc = value;
330             }
331         }
332     }
333 
334     seg->e.seginfo->current_loc = value;
335     seg->e.seginfo->written = FALSE;
336 
337     if( seg->e.seginfo->current_loc > seg->sym.max_offset )
338         seg->sym.max_offset = seg->e.seginfo->current_loc;
339 
340     return( NOT_ERROR );
341 }
342 
343 /* write object module */
344 
WriteModule(struct module_info * modinfo)345 static ret_code WriteModule( struct module_info *modinfo )
346 /********************************************************/
347 {
348     struct dsym *curr;
349 
350     DebugMsg(("WriteModule enter\n"));
351 
352     /* final checks */
353     /* check limit of segments */
354     for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
355         if ( curr->e.seginfo->Ofssize == USE16 && curr->sym.max_offset > 0x10000 ) {
356             if ( Options.output_format == OFORMAT_OMF )
357                 EmitErr( SEGMENT_EXCEEDS_64K_LIMIT, curr->sym.name );
358             else
359                 EmitWarn( 2, SEGMENT_EXCEEDS_64K_LIMIT, curr->sym.name );
360         }
361     }
362 
363     modinfo->g.WriteModule( modinfo );
364 
365 #if DLLIMPORT
366     /* is the -Fd option given with a file name? */
367     if ( Options.names[OPTN_LNKDEF_FN] ) {
368         FILE *ld;
369         ld = fopen( Options.names[OPTN_LNKDEF_FN], "w" );
370         if ( ld == NULL ) {
371             return( EmitErr( CANNOT_OPEN_FILE, Options.names[OPTN_LNKDEF_FN], ErrnoStr() ) );
372         }
373         for ( curr = SymTables[TAB_EXT].head; curr != NULL ; curr = curr->next ) {
374             DebugMsg(("WriteModule: ext=%s, isproc=%u, weak=%u\n", curr->sym.name, curr->sym.isproc, curr->sym.weak ));
375             if ( curr->sym.isproc && ( curr->sym.weak == FALSE || curr->sym.iat_used ) &&
376                 curr->sym.dll && *(curr->sym.dll->name) != NULLC ) {
377                 int size;
378                 Mangle( &curr->sym, StringBufferEnd );
379                 size = sprintf( CurrSource, "import '%s'  %s.%s\n", StringBufferEnd, curr->sym.dll->name, curr->sym.name );
380                 if ( fwrite( CurrSource, 1, size, ld ) != size )
381                     WriteError();
382             }
383         }
384         fclose( ld );
385     }
386 #endif
387     DebugMsg(("WriteModule exit\n"));
388     return( NOT_ERROR );
389 }
390 
391 #define is_valid_first_char( ch )  ( isalpha(ch) || ch=='_' || ch=='@' || ch=='$' || ch=='?' || ch=='.' )
392 
393 /* check name of text macros defined via -D option */
394 
is_valid_identifier(char * id)395 static int is_valid_identifier( char *id )
396 /****************************************/
397 {
398     /* special handling of first char of an id: it can't be a digit,
399      but can be a dot (don't care about ModuleInfo.dotname!). */
400 
401     if( is_valid_first_char( *id ) == 0 )
402         return( ERROR );
403     id++;
404     for( ; *id != NULLC; id++ ) {
405         if ( is_valid_id_char( *id ) == FALSE )
406             return( ERROR );
407     }
408     /* don't allow a single dot! */
409     if ( *(id-1) == '.' )
410         return( ERROR );
411 
412     return( NOT_ERROR );
413 }
414 
415 /* add text macros defined with the -D cmdline switch */
416 
add_cmdline_tmacros(void)417 static void add_cmdline_tmacros( void )
418 /****************************************/
419 {
420     struct qitem *p;
421     char *name;
422     char *value;
423     int len;
424     struct asym *sym;
425 
426     DebugMsg(("add_cmdline_tmacros enter\n"));
427     for ( p = Options.queues[OPTQ_MACRO]; p; p = p->next ) {
428         DebugMsg(("add_cmdline_tmacros: found >%s<\n", p->value));
429         name = p->value;
430         value = strchr( name, '=' );
431         if( value == NULL ) {
432             /* v2.06: ensure that 'value' doesn't point to r/o space */
433             //value = "";
434             value = name + strlen( name ); /* use the terminating NULL */
435         } else {
436             len = value - name;
437             name = (char *)myalloca( len + 1 );
438             memcpy( name, p->value, len );
439             *(name + len) = NULLC;
440             value++;
441         }
442 
443         /* there's no check whether the name is a reserved word!
444          */
445         if( is_valid_identifier( name ) == ERROR ) {
446             DebugMsg(("add_cmdline_tmacros: name >%s< invalid\n", name ));
447             EmitErr( SYNTAX_ERROR_EX, name );
448         } else {
449             sym = SymSearch( name );
450             if ( sym == NULL ) {
451                 sym = SymCreate( name );
452                 sym->state = SYM_TMACRO;
453             }
454             if ( sym->state == SYM_TMACRO ) {
455                 sym->isdefined = TRUE;
456                 sym->predefined = TRUE;
457                 sym->string_ptr = value;
458             } else
459                 EmitErr( SYMBOL_ALREADY_DEFINED, name );
460         }
461     }
462     return;
463 }
464 
465 /* add the include paths set by -I option */
466 
add_incpaths(void)467 static void add_incpaths( void )
468 /******************************/
469 {
470     struct qitem *p;
471     DebugMsg(("add_incpaths: enter\n"));
472     for ( p = Options.queues[OPTQ_INCPATH]; p; p = p->next ) {
473         AddStringToIncludePath( p->value );
474     }
475 }
476 
477 /* this is called for every pass.
478  * symbol table and ModuleInfo are initialized.
479  */
CmdlParamsInit(int pass)480 static void CmdlParamsInit( int pass )
481 /************************************/
482 {
483     DebugMsg(("CmdlParamsInit(%u) enter\n", pass));
484 
485 #if BUILD_TARGET
486     if ( pass == PASS_1 ) {
487         struct asym *sym;
488         char *tmp;
489         char *p;
490 
491         _strupr( Options.build_target );
492         tmp = myalloca( strlen( Options.build_target ) + 5 ); /* null + 4 uscores */
493         strcpy( tmp, uscores );
494         strcat( tmp, Options.build_target );
495         strcat( tmp, uscores );
496 
497         /* define target */
498         sym = CreateVariable( tmp, 0 );
499         sym->predefined = TRUE;
500 
501         p = NULL;
502         if( _stricmp( Options.build_target, "DOS" ) == 0 ) {
503             p = "__MSDOS__";
504         } else if( _stricmp( Options.build_target, "NETWARE" ) == 0 ) {
505             if( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
506                 p = "__NETWARE_386__";
507             } else {
508                 /* do nothing ... __NETWARE__ already defined */
509             }
510         } else if( _stricmp( Options.build_target, "WINDOWS" ) == 0 ) {
511             if( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
512                 p = "__WINDOWS_386__";
513             } else {
514                 /* do nothing ... __WINDOWS__ already defined */
515             }
516         } else if( _stricmp( Options.build_target, "QNX" ) == 0 ) {
517             p = "__UNIX__";
518         } else if( _stricmp( Options.build_target, "LINUX" ) == 0 ) {
519             p = "__UNIX__";
520         }
521         if ( p ) {
522             sym = CreateVariable( p, 0 );
523             sym->predefined = TRUE;
524         }
525     }
526 #endif
527 
528     if ( pass == PASS_1 ) {
529         char *env;
530         /* v2.06: this is done in ModulePassInit now */
531         //SetCPU( Options.cpu );
532         add_cmdline_tmacros();
533         add_incpaths();
534         if ( Options.ignore_include == FALSE )
535             if ( env = getenv( "INCLUDE" ) )
536                 AddStringToIncludePath( env );
537     }
538     DebugMsg(("CmdlParamsInit exit\n"));
539     return;
540 }
541 
WritePreprocessedLine(const char * string)542 void WritePreprocessedLine( const char *string )
543 /**********************************************/
544 /* print out preprocessed source lines
545  */
546 {
547     static bool PrintEmptyLine = TRUE;
548     const char *p;
549 
550 #if 0 /* v2.08: removed, obsolete */
551     /* filter some macro specific directives */
552     if ( tokenarray[0].token == T_DIRECTIVE &&
553          ( tokenarray[0].tokval == T_ENDM ||
554            tokenarray[0].tokval == T_EXITM))
555         return;
556     /* don't print generated code - with one exception:
557      if the code was generated as a result of structure initialization,
558      then do!
559      */
560     if ( GeneratedCode )
561         return;
562 #endif
563     if ( Token_Count > 0 ) {
564         /* v2.08: don't print a leading % (this char is no longer filtered) */
565         for ( p = string; isspace( *p ); p++ );
566         printf("%s\n", *p == '%' ? p+1 : string );
567         PrintEmptyLine = TRUE;
568     } else if ( PrintEmptyLine ) {
569         PrintEmptyLine = FALSE;
570         printf("\n");
571     }
572 }
573 
574 /* set Masm v5.1 compatibility options */
575 
SetMasm510(bool value)576 void SetMasm510( bool value )
577 /***************************/
578 {
579     ModuleInfo.m510 = value;
580     ModuleInfo.oldstructs = value;
581     /* ModuleInfo.oldmacros = value; not implemented yet */
582     ModuleInfo.dotname = value;
583     ModuleInfo.setif2 = value;
584 
585     if ( value ) {
586         if ( ModuleInfo.model == MODEL_NONE ) {
587             /* if no model is specified, set OFFSET:SEGMENT */
588             ModuleInfo.offsettype = OT_SEGMENT;
589             if ( ModuleInfo.langtype == LANG_NONE ) {
590                 ModuleInfo.scoped = FALSE;
591                 ModuleInfo.procs_private = TRUE;
592             }
593         }
594     }
595     return;
596 }
597 
598 /* called for each pass */
599 
ModulePassInit(void)600 static void ModulePassInit( void )
601 /********************************/
602 {
603     enum cpu_info cpu = Options.cpu;
604     enum model_type model = Options.model;
605 #if DLLIMPORT
606     struct dsym *curr;
607 #endif
608 
609     DebugMsg(( "ModulePassInit() enter\n" ));
610     /* set default values not affected by the masm 5.1 compat switch */
611     ModuleInfo.procs_private = FALSE;
612     ModuleInfo.procs_export = FALSE;
613     ModuleInfo.offsettype = OT_GROUP;
614     ModuleInfo.scoped = TRUE;
615 
616 
617 #if FASTPASS
618     /* v2.03: don't generate the code if fastpass is active */
619     /* v2.08: query UseSavedState instead of StoreState */
620     //if ( StoreState == FALSE ) {
621     if ( UseSavedState == FALSE ) {
622 #endif
623         ModuleInfo.langtype = Options.langtype;
624         ModuleInfo.fctype = Options.fctype;
625 #if AMD64_SUPPORT
626         if ( ModuleInfo.sub_format == SFORMAT_64BIT ) {
627             /* v2.06: force cpu to be at least P_64, without side effect to Options.cpu */
628             if ( ( cpu &  P_CPU_MASK ) < P_64 ) /* enforce cpu to be 64-bit */
629                 cpu = P_64;
630             /* ignore -m switch for 64-bit formats.
631              * there's no other model than FLAT possible.
632              */
633             model = MODEL_FLAT;
634             if ( ModuleInfo.langtype == LANG_NONE && Options.output_format == OFORMAT_COFF )
635                 ModuleInfo.langtype = LANG_FASTCALL;
636         } else
637 #endif
638             /* if model FLAT is to be set, ensure that cpu is compat. */
639             if ( model == MODEL_FLAT && ( cpu & P_CPU_MASK ) < P_386 ) /* cpu < 386? */
640                 cpu = P_386;
641 
642         SetCPU( cpu );
643         /* table ModelToken starts with MODEL_TINY, which is index 1" */
644         if ( model != MODEL_NONE )
645             AddLineQueueX( "%r %s", T_DOT_MODEL, ModelToken[model - 1] );
646 
647 #if FASTPASS
648     }
649 #endif
650 
651     SetMasm510( Options.masm51_compat );
652     ModuleInfo.defOfssize = USE16;
653     ModuleInfo.ljmp     = TRUE;
654 
655     ModuleInfo.list   = Options.write_listing;
656     ModuleInfo.cref   = TRUE;
657     ModuleInfo.listif = Options.listif;
658     ModuleInfo.list_generated_code = Options.list_generated_code;
659     ModuleInfo.list_macro = Options.list_macro;
660 
661     ModuleInfo.case_sensitive = Options.case_sensitive;
662     ModuleInfo.convert_uppercase = Options.convert_uppercase;
663     SymSetCmpFunc();
664 
665     ModuleInfo.segorder = SEGORDER_SEQ;
666     ModuleInfo.radix = 10;
667     ModuleInfo.fieldalign = Options.fieldalign;
668 #if PROCALIGN
669     ModuleInfo.procalign = 0;
670 #endif
671 #if DLLIMPORT
672     /* if OPTION DLLIMPORT was used, reset all iat_used flags */
673     if ( ModuleInfo.g.DllQueue )
674         for ( curr = SymTables[TAB_EXT].head; curr; curr = curr->next )
675             curr->sym.iat_used = FALSE;
676 #endif
677 }
678 
679 #if 0 /* v2.07: removed */
680 /* scan - and clear - global queue (EXTERNDEFs).
681  * items which have been defined within the module
682  * will become public.
683  * PROTOs aren't included in the global queue.
684  * They will become public when - and if - the PROC directive
685  * for the symbol is met.
686  */
687 static void scan_globals( void )
688 /******************************/
689 {
690     struct qnode    *curr;
691     struct qnode    *next;
692     struct asym     *sym;
693 
694     /* turn EXTERNDEFs into PUBLICs if defined in the module.
695      * PROCs are handled differently - so ignore these entries here!
696      */
697     /* obsolete since v2.07.
698      * it's simpler and better to make the symbol public if it turns
699      * from SYM_EXTERNAL to SYM_INTERNAL.
700      * the other case, that is, the EXTERNDEF comes AFTER the definition,
701      * is handled in ExterndefDirective()
702      */
703     DebugMsg(("scan_globals: GlobalQueue=%X\n", ModuleInfo.g.GlobalQueue));
704     for ( curr = ModuleInfo.g.GlobalQueue.head; curr; curr = next ) {
705         next = curr->next;
706         sym = (struct asym *)curr->elmt;
707         DebugMsg(("scan_globals: %s state=%u used=%u public=%u\n", sym->name, sym->state, sym->used, sym->public ));
708         if( sym->state == SYM_INTERNAL && sym->public == FALSE && sym->isproc == FALSE ) {
709             /* add it to the public queue */
710             sym->public = TRUE;
711             QEnqueue( &ModuleInfo.g.PubQueue, curr );
712             DebugMsg(("scan_globals: %s added to public queue\n", sym->name ));
713             continue; /* don't free this item! */
714         }
715         LclFree( curr );
716     }
717     /* the queue is empty now */
718     ModuleInfo.g.GlobalQueue.head = NULL;
719 }
720 #endif
721 
722 /* checks after pass one has been finished without errors */
723 
PassOneChecks(void)724 static void PassOneChecks( void )
725 /*******************************/
726 {
727     struct dsym *curr;
728     struct dsym *next;
729     struct qnode *q;
730     struct qnode *qn;
731 #ifdef DEBUG_OUT
732     int cntUnusedExt = 0;
733 #endif
734 
735     /* check for open structures and segments has been done inside the
736      * END directive handling already
737      * v2.10: now done for PROCs as well, since procedures
738      * must be closed BEFORE segments are to be closed.
739      */
740     //ProcCheckOpen();
741     HllCheckOpen();
742     CondCheckOpen();
743 
744     if( ModuleInfo.EndDirFound == FALSE )
745         EmitError( END_DIRECTIVE_REQUIRED );
746 
747 #ifdef DEBUG_OUT
748     for ( curr = SymTables[TAB_UNDEF].head; curr; curr = curr->next ) {
749         DebugMsg(("PassOneChecks: undefined symbol %s\n", curr->sym.name ));
750     }
751 #endif
752     /* v2.04: check the publics queue.
753      * - only internal symbols can be public.
754      * - weak external symbols are filtered ( since v2.11 )
755      * - anything else is an error
756      * v2.11: moved here ( from inside the "#if FASTPASS"-block )
757      * because the loop will now filter weak externals [ this
758      * was previously done in GetPublicSymbols() ]
759      */
760     for( q = ModuleInfo.g.PubQueue.head, qn = (struct qnode *)&ModuleInfo.g.PubQueue ; q; q = q->next ) {
761 
762         if ( q->sym->state == SYM_INTERNAL )
763             qn = q;
764         else if ( q->sym->state == SYM_EXTERNAL && q->sym->weak == TRUE ) {
765             DebugMsg(("PassOneChecks: public for weak external skipped: %s\n", q->sym->name ));
766             qn->next = q->next;
767             LclFree( q );
768             q = qn;
769         } else {
770             DebugMsg(("PassOneChecks: invalid public attribute for %s [state=%u weak=%u]\n", q->sym->name, q->sym->state, q->sym->weak ));
771 #if FASTPASS
772             SkipSavedState();
773 #endif
774             break;
775         }
776     }
777 #if FASTPASS
778     /* to force a full second pass in case of missing symbols,
779      * activate the next block. It was implemented to have proper
780      * error displays if a forward reference wasn't found.
781      * However, v1.95 final won't need this anymore, because both
782      * filename + lineno for every line is known now in pass 2.
783      *
784      * v2.13: if the undefined symbol occurs in a macro, there's
785      * still a problem, since macro names aren't stored in the
786      * line store. So the block is activated again.
787      */
788 #if 0
789     if ( SymTables[TAB_UNDEF].head ) {
790         DebugMsg(("PassOneChecks: undefined symbols exist, first=%s\n", SymTables[TAB_UNDEF].head->sym.name ));
791         SkipSavedState();
792     }
793 #endif
794     /* check if there's an undefined segment reference.
795      * This segment was an argument to a group definition then.
796      * Just do a full second pass, the GROUP directive will report
797      * the error.
798      */
799     for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
800         if( curr->sym.segment == NULL ) {
801             DebugMsg(("PassOneChecks: undefined segment %s\n", curr->sym.name ));
802             SkipSavedState();
803             break;
804         }
805     }
806 #if COFF_SUPPORT
807     /* if there's an item in the safeseh list which is not an
808      * internal proc, make a full second pass to emit a proper
809      * error msg at the .SAFESEH directive
810      */
811     for ( q = ModuleInfo.g.SafeSEHQueue.head; q; q = q->next ) {
812         if ( q->sym->state != SYM_INTERNAL || q->sym->isproc == FALSE ) {
813             SkipSavedState();
814             break;
815         }
816     }
817 #endif
818 
819     /* scan ALIASes for COFF/ELF */
820 
821 #if COFF_SUPPORT || ELF_SUPPORT
822     if ( Options.output_format == OFORMAT_COFF
823 #if ELF_SUPPORT
824         || Options.output_format == OFORMAT_ELF
825 #endif
826        ) {
827         for( curr = SymTables[TAB_ALIAS].head ; curr != NULL ;curr = curr->next ) {
828             struct asym *sym;
829             sym = curr->sym.substitute;
830             /* check if symbol is external or public */
831             if ( sym == NULL ||
832                 ( sym->state != SYM_EXTERNAL &&
833                  ( sym->state != SYM_INTERNAL || sym->ispublic == FALSE ))) {
834                 SkipSavedState();
835                 break;
836             }
837             /* make sure it becomes a strong external */
838             if ( sym->state == SYM_EXTERNAL )
839                 sym->used = TRUE;
840         }
841     }
842 #endif
843 
844 #endif /* FASTPASS */
845 
846     /* scan the EXTERN/EXTERNDEF items */
847 
848     for( curr = SymTables[TAB_EXT].head ; curr; curr = next ) {
849         next = curr->next;
850         /* v2.01: externdefs which have been "used" become "strong" */
851         if ( curr->sym.used )
852             curr->sym.weak = FALSE;
853         /* remove unused EXTERNDEF/PROTO items from queue. */
854         if ( curr->sym.weak == TRUE
855 #if DLLIMPORT
856             && curr->sym.iat_used == FALSE
857 #endif
858            ) {
859             sym_remove_table( &SymTables[TAB_EXT], curr );
860 #ifdef DEBUG_OUT
861             cntUnusedExt++;
862 #endif
863             continue;
864         }
865 
866 #if FASTMEM==0
867         /* v2.05: clear fixup list (used for backpatching in pass one) */
868         if ( curr->sym.bp_fixup ) {
869             struct fixup *c;
870             struct fixup *n;
871             for( c = curr->sym.bp_fixup ; c; ) {
872                 n = c->nextbp;
873                 LclFree( c );
874                 c = n;
875             }
876             curr->sym.bp_fixup = NULL;
877         }
878 #endif
879 
880         if ( curr->sym.iscomm == TRUE )
881             continue;
882         /* optional alternate symbol must be INTERNAL or EXTERNAL.
883          * COFF ( and ELF? ) also wants internal symbols to be public
884          * ( which is reasonable, since the linker won't know private
885          * symbols and hence will search for a symbol of that name
886          * "elsewhere" ).
887          */
888 #if FASTPASS
889         if ( curr->sym.altname ) {
890             if ( curr->sym.altname->state == SYM_INTERNAL ) {
891 #if COFF_SUPPORT || ELF_SUPPORT
892                 /* for COFF/ELF, the altname must be public or external */
893                 if ( curr->sym.altname->ispublic == FALSE &&
894                     ( Options.output_format == OFORMAT_COFF
895 #if ELF_SUPPORT
896                      || Options.output_format == OFORMAT_ELF
897 #endif
898                     ) ) {
899                     SkipSavedState();
900                 }
901 #endif
902             } else if ( curr->sym.altname->state != SYM_EXTERNAL ) {
903                 /* do not use saved state, scan full source in second pass */
904                 SkipSavedState();
905             }
906         }
907 #endif
908     }
909 
910 #ifdef DEBUG_OUT
911     DebugMsg(("PassOneChecks: removed unused externals: %u\n", cntUnusedExt ));
912     DebugMsg(("PassOneChecks: forward references:\n"));
913     for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
914         int i;
915         int j;
916         struct asym * sym;
917         struct fixup * fix;
918         for ( i = 0, j = 0, sym = curr->e.seginfo->label_list; sym; sym = (struct asym *)((struct dsym *)sym)->next ) {
919             i++;
920             for ( fix = sym->bp_fixup; fix ; fix = fix->nextbp, j++ );
921         }
922         DebugMsg(("PassOneChecks: segm=%s, labels=%u forward refs=%u\n", curr->sym.name, i, j));
923     }
924 #endif
925 
926     if ( ModuleInfo.g.error_count == 0 ) {
927 
928         /* make all symbols of type SYM_INTERNAL, which aren't
929          a constant, public.  */
930         if ( Options.all_symbols_public )
931             SymMakeAllSymbolsPublic();
932 
933         if ( Options.syntax_check_only == FALSE )
934             write_to_file = TRUE;
935 
936         if ( ModuleInfo.g.Pass1Checks )
937             ModuleInfo.g.Pass1Checks( &ModuleInfo );
938     }
939 
940 
941     return;
942 }
943 
944 /* do ONE assembly pass
945  * the FASTPASS variant (which is default now) doesn't scan the full source
946  * for each pass. For this to work, the following things are implemented:
947  * 1. in pass one, save state if the first byte is to be emitted.
948  *    <state> is the segment stack, moduleinfo state, ...
949  * 2. once the state is saved, all preprocessed lines must be stored.
950  *    this can be done here, in OnePass, the line is in <string>.
951  *    Preprocessed macro lines are stored in RunMacro().
952  * 3. for subsequent passes do
953  *    - restore the state
954  *    - read preprocessed lines and feed ParseLine() with it
955  */
OnePass(void)956 static int OnePass( void )
957 /************************/
958 {
959 
960     InputPassInit();
961     ModulePassInit();
962     SymPassInit( Parse_Pass );
963     LabelInit();
964     SegmentInit( Parse_Pass );
965     ContextInit( Parse_Pass );
966     ProcInit();
967     TypesInit();
968     HllInit( Parse_Pass );
969     MacroInit( Parse_Pass ); /* insert predefined macros */
970     AssumeInit( Parse_Pass );
971     CmdlParamsInit( Parse_Pass );
972 
973     ModuleInfo.EndDirFound = FALSE;
974     ModuleInfo.PhaseError = FALSE;
975     //Modend = FALSE;
976     /* LineNumber = 0; */
977     LinnumInit();
978 
979 #ifdef DEBUG_OUT
980     if ( Parse_Pass > PASS_1 ) {
981         DebugMsg(("OnePass(%u) segments (current=%s):\n", Parse_Pass + 1, CurrSeg ? CurrSeg->sym.name : "NULL" ));
982         {
983             struct dsym *dir;
984             for( dir = SymTables[TAB_SEG].head; dir; dir = dir->next ) {
985                 DebugMsg(("OnePass(%u): segm=%-8s typ=%X start=%8X max_ofs=%8X\n", Parse_Pass + 1,
986                           dir->sym.name, dir->e.seginfo->segtype, dir->e.seginfo->start_loc, dir->sym.max_offset ));
987             }
988         }
989     }
990 #endif
991     /* the functions above might have written something to the line queue */
992     if ( is_linequeue_populated() )
993         RunLineQueue();
994 #if FASTPASS
995     StoreState = FALSE;
996     if ( Parse_Pass > PASS_1 && UseSavedState == TRUE ) {
997         LineStoreCurr = RestoreState();
998         while ( LineStoreCurr && ModuleInfo.EndDirFound == FALSE ) {
999             /* the source line is modified in Tokenize() if it contains a comment! */
1000 #if USELSLINE==0
1001             strcpy( CurrSource, LineStoreCurr->line );
1002 #endif
1003             set_curr_srcfile( LineStoreCurr->srcfile, LineStoreCurr->lineno );
1004             /* v2.06: list flags now initialized on the top level */
1005             ModuleInfo.line_flags = 0;
1006             MacroLevel = ( LineStoreCurr->srcfile == 0xFFF ? 1 : 0 );
1007             DebugMsg1(("OnePass(%u) cur/nxt=%X/%X src=%X(%s).%u mlvl=%u: >%s<\n",
1008                 Parse_Pass+1, LineStoreCurr, LineStoreCurr->next, LineStoreCurr->srcfile, GetTopSrcName(),
1009                 LineStoreCurr->lineno, MacroLevel, LineStoreCurr->line ));
1010             ModuleInfo.CurrComment = NULL; /* v2.08: added (var is never reset because GetTextLine() isn't called) */
1011 #if USELSLINE
1012             if ( Token_Count = Tokenize( LineStoreCurr->line, 0, ModuleInfo.tokenarray, TOK_DEFAULT ) )
1013 #else
1014             if ( Token_Count = Tokenize( CurrSource, 0, ModuleInfo.tokenarray, TOK_DEFAULT ) )
1015 #endif
1016                 ParseLine( ModuleInfo.tokenarray );
1017             LineStoreCurr = LineStoreCurr->next;
1018         }
1019     } else
1020 #endif
1021     {
1022         struct qitem *pq;
1023         /* v2.11: handle -Fi files here ( previously in CmdlParamsInit ) */
1024         for ( pq = Options.queues[OPTQ_FINCLUDE]; pq; pq = pq->next ) {
1025             DebugMsg(("OnePass: force include of file: %s\n", pq->value ));
1026             if ( SearchFile( pq->value, TRUE ) )
1027                 ProcessFile( ModuleInfo.tokenarray );
1028         }
1029         ProcessFile( ModuleInfo.tokenarray ); /* process the main source file */
1030     }
1031 
1032     LinnumFini();
1033 
1034     if ( Parse_Pass == PASS_1 )
1035         PassOneChecks();
1036 
1037     ClearSrcStack();
1038 
1039     return( 1 );
1040 }
1041 
1042 #if BUILD_TARGET
1043 /*
1044  * from WASM : get os-specific xxx_INCLUDE environment variable.
1045  *             if set, add string to include path.
1046  */
1047 
get_os_include(void)1048 static void get_os_include( void )
1049 /********************************/
1050 {
1051     char *env;
1052     char *tmp;
1053 
1054     /* add OS_include to the include path */
1055 
1056     tmp = myalloca( strlen( Options.build_target ) + 10 );
1057     strcpy( tmp, Options.build_target );
1058     strcat( tmp, "_INCLUDE" );
1059 
1060     env = getenv( tmp );
1061     if( env != NULL ) {
1062         AddStringToIncludePath( env );
1063     }
1064 }
1065 
1066 #endif
1067 
get_module_name(void)1068 static void get_module_name( void )
1069 /*********************************/
1070 {
1071     //char dummy[_MAX_EXT];
1072     char        *p;
1073 
1074     /* v2.08: prefer name given by -nm option */
1075     if ( Options.names[OPTN_MODULE_NAME] ) {
1076         strncpy( ModuleInfo.name, Options.names[OPTN_MODULE_NAME], sizeof( ModuleInfo.name ) );
1077         ModuleInfo.name[ sizeof( ModuleInfo.name ) - 1] = NULLC;
1078     } else {
1079         /* v2.12: _splitpath()/_makepath() removed */
1080         const char *fn = GetFNamePart( CurrFName[ASM] );
1081         char *ext = GetExtPart( fn );
1082         memcpy( ModuleInfo.name, fn, ext - fn );
1083         ModuleInfo.name[ ext - fn ] = NULLC;
1084         //_splitpath( CurrFName[ASM], NULL, NULL, ModuleInfo.name, dummy );
1085     }
1086 
1087     _strupr( ModuleInfo.name );
1088     /* the module name must be a valid identifier, because it's used
1089      * as part of a segment name in certain memory models.
1090      */
1091     for( p = ModuleInfo.name; *p; ++p ) {
1092         if( !( isalnum( *p ) || ( *p == '_' ) || ( *p == '$' )
1093             || ( *p == '@' ) || ( *p == '?') ) ) {
1094             /* it's not a legal character for a symbol name */
1095             *p = '_';
1096         }
1097     }
1098     /* first character can't be a digit either */
1099     if( isdigit( ModuleInfo.name[0] ) ) {
1100         ModuleInfo.name[0] = '_';
1101     }
1102 }
1103 
1104 /* called by AssembleInit(), once per source module.
1105  * symbol table has been initialized here.
1106  */
ModuleInit(void)1107 static void ModuleInit( void )
1108 /****************************/
1109 {
1110     ModuleInfo.sub_format = Options.sub_format;
1111     ModuleInfo.fmtopt = &formatoptions[Options.output_format];
1112     ModuleInfo.CommentDataInCode = (Options.output_format == OFORMAT_OMF &&
1113                          Options.no_comment_data_in_code_records == FALSE);
1114     ModuleInfo.g.error_count = 0;
1115     ModuleInfo.g.warning_count = 0;
1116     ModuleInfo.model = MODEL_NONE;
1117     /* ModuleInfo.distance = STACK_NONE; */
1118     ModuleInfo.ostype = OPSYS_DOS;
1119     ModuleInfo.emulator = (Options.floating_point == FPO_EMULATION);
1120     //ModuleInfo.flatgrp_idx = 0;
1121 
1122     get_module_name(); /* set ModuleInfo.name */
1123 
1124     /* v2.06: ST_PROC has been removed */
1125     //SimpleType[ST_PROC].mem_type = MT_NEAR;
1126 
1127     memset( SymTables, 0, sizeof( SymTables[0] ) * TAB_LAST );
1128     ModuleInfo.fmtopt->init( &ModuleInfo );
1129 
1130     return;
1131 }
1132 
ReswTableInit(void)1133 static void ReswTableInit( void )
1134 /*******************************/
1135 {
1136     ResWordsInit();
1137     if ( Options.output_format == OFORMAT_OMF ) {
1138         /* DebugMsg(("InitAsm: disable IMAGEREL+SECTIONREL\n")); */
1139         /* for OMF, IMAGEREL and SECTIONREL are disabled */
1140 #if IMAGERELSUPP
1141         DisableKeyword( T_IMAGEREL );
1142 #endif
1143 #if SECTIONRELSUPP
1144         DisableKeyword( T_SECTIONREL );
1145 #endif
1146     }
1147 
1148     if ( Options.strict_masm_compat == TRUE ) {
1149         DebugMsg(("ReswTableInit: disable INCBIN + FASTCALL keywords\n"));
1150         DisableKeyword( T_INCBIN );
1151         DisableKeyword( T_FASTCALL );
1152     }
1153 
1154     return;
1155 }
1156 
open_files(void)1157 static void open_files( void )
1158 /****************************/
1159 {
1160     /* open ASM file */
1161     DebugMsg(("open_files() enter\n" ));
1162 
1163     //memset( CurrFile, 0, sizeof( CurrFile ) );
1164     /* CurrFile[ASM] = fopen( CurrFName[ASM], "r" ); */
1165     CurrFile[ASM] = fopen( CurrFName[ASM], "rb" );
1166     if( CurrFile[ASM] == NULL ) {
1167         DebugMsg(("open_files(): cannot open source file, fopen(\"%s\") failed\n", CurrFName[ASM] ));
1168         Fatal( CANNOT_OPEN_FILE, CurrFName[ASM], ErrnoStr() );
1169     }
1170 
1171     /* open OBJ file */
1172     if ( Options.syntax_check_only == FALSE ) {
1173         CurrFile[OBJ] = fopen( CurrFName[OBJ], "wb" );
1174         if( CurrFile[OBJ] == NULL ) {
1175             DebugMsg(("open_files(): cannot open object file, fopen(\"%s\") failed\n", CurrFName[OBJ] ));
1176             Fatal( CANNOT_OPEN_FILE, CurrFName[OBJ], ErrnoStr() );
1177         }
1178         DebugMsg(("open_files(): output, fopen(\"%s\") ok\n", CurrFName[OBJ] ));
1179     }
1180 
1181     if( Options.write_listing ) {
1182         CurrFile[LST] = fopen( CurrFName[LST], "wb" );
1183         if ( CurrFile[LST] == NULL )
1184             Fatal( CANNOT_OPEN_FILE, CurrFName[LST], ErrnoStr() );
1185     }
1186     return;
1187 }
1188 
close_files(void)1189 void close_files( void )
1190 /**********************/
1191 {
1192     /* v2.11: no fatal errors anymore if fclose() fails.
1193      * That's because Fatal() may cause close_files() to be
1194      * reentered and thus cause an endless loop.
1195      */
1196 
1197     /* close ASM file */
1198     if( CurrFile[ASM] != NULL ) {
1199         if( fclose( CurrFile[ASM] ) != 0 )
1200             EmitErr( CANNOT_CLOSE_FILE, CurrFName[ASM], errno );
1201         CurrFile[ASM] = NULL;
1202     }
1203 
1204     /* close OBJ file */
1205     if ( CurrFile[OBJ] != NULL ) {
1206         if ( fclose( CurrFile[OBJ] ) != 0 )
1207             EmitErr( CANNOT_CLOSE_FILE, CurrFName[OBJ], errno );
1208         CurrFile[OBJ] = NULL;
1209     }
1210     /* delete the object module if errors occured */
1211     if ( Options.syntax_check_only == FALSE &&
1212         ModuleInfo.g.error_count > 0 ) {
1213         remove( CurrFName[OBJ] );
1214     }
1215 
1216     if( CurrFile[LST] != NULL ) {
1217         fclose( CurrFile[LST] );
1218         CurrFile[LST] = NULL;
1219     }
1220 
1221     /* close ERR file */
1222     if ( CurrFile[ERR] != NULL ) {
1223         fclose( CurrFile[ERR] );
1224         CurrFile[ERR] = NULL;
1225     } else if ( CurrFName[ERR] )
1226         /* nothing written, delete any existing ERR file */
1227         remove( CurrFName[ERR] );
1228     return;
1229 }
1230 
1231 /* get default file extension for error, object and listing files */
1232 
GetExt(int type)1233 static char *GetExt( int type )
1234 /*****************************/
1235 {
1236     switch ( type ) {
1237     case OBJ:
1238 #if BIN_SUPPORT
1239         if ( Options.output_format == OFORMAT_BIN )
1240 #if MZ_SUPPORT || PE_SUPPORT
1241             if ( Options.sub_format == SFORMAT_MZ
1242 #if PE_SUPPORT
1243                 || Options.sub_format == SFORMAT_PE
1244 #endif
1245                )
1246                 return( EXE_EXT );
1247             else
1248 #endif
1249                 return( BIN_EXT );
1250 #endif
1251         return( OBJ_EXT );
1252     case LST:
1253         return( LST_EXT );
1254     case ERR:
1255         return( ERR_EXT );
1256     }
1257     return( NULL );
1258 }
1259 
1260 /* set filenames for .obj, .lst and .err
1261  * in:
1262  *  name: assembly source name
1263  *  DefaultDir[]: default directory part for .obj, .lst and .err
1264  * in:
1265  *  CurrFName[] for .obj, .lst and .err ( may be NULL )
1266  * v2.12: _splitpath()/_makepath() removed.
1267  */
1268 
SetFilenames(const char * name)1269 static void SetFilenames( const char *name )
1270 /******************************************/
1271 {
1272     int i;
1273     const char *fn;
1274     char *ext;
1275     char path[ FILENAME_MAX ];
1276 
1277     DebugMsg(("SetFilenames(\"%s\") enter\n", name ));
1278 
1279     /* set CurrFName[ASM] */
1280     CurrFName[ASM] = LclAlloc( strlen( name ) + 1 );
1281     strcpy( CurrFName[ASM], name );
1282 
1283     /* set [OBJ], [ERR], [LST] */
1284     fn = GetFNamePart( name );
1285     for ( i = ASM+1; i < NUM_FILE_TYPES; i++ ) {
1286         if( Options.names[i] == NULL ) {
1287             path[0] = NULLC;
1288             if ( DefaultDir[i])
1289                 strcpy( path, DefaultDir[i] );
1290             strcat( path, fn );
1291             ext = GetExtPart( path );
1292             *ext++  = '.';
1293             strcpy( ext, GetExt( i ) );
1294 
1295         } else {
1296             /* filename has been set by cmdline option -Fo, -Fl or -Fr */
1297             const char *fn2;
1298             strcpy( path, Options.names[i] );
1299             fn2 = GetFNamePart( path );
1300             if( *fn2 == NULLC )
1301                 strcpy( (char *)fn2, fn );
1302             ext = GetExtPart( fn2 );
1303             if( *ext == NULLC ) {
1304                 *ext++  = '.';
1305                 strcpy( ext, GetExt( i ) );
1306             }
1307         }
1308         DebugMsg(("SetFilenames: i=%u >%s<\n", i, path ));
1309         CurrFName[i] = LclAlloc( strlen( path ) + 1 );
1310         strcpy( CurrFName[i], path );
1311     }
1312     return;
1313 }
1314 
1315 /* init assembler. called once per module */
1316 
AssembleInit(const char * source)1317 static void AssembleInit( const char *source )
1318 /********************************************/
1319 {
1320     DebugMsg(("AssembleInit(\"%s\") enter\n", source ));
1321 
1322     MemInit();
1323     //start_label   = NULL;
1324     //start_displ   = 0;
1325     write_to_file = FALSE;
1326     //GeneratedCode = 0;
1327     LinnumQueue.head = NULL;
1328 
1329     SetFilenames( source );
1330 
1331 #if FASTPASS
1332     FastpassInit();
1333 #endif
1334     open_files();
1335 #if BUILD_TARGET
1336     get_os_include();
1337 #endif
1338     ReswTableInit();
1339     SymInit();
1340     InputInit();
1341 
1342     ModuleInit();
1343     CondInit();
1344     ExprEvalInit();
1345     LstInit();
1346 
1347     DebugMsg(("AssembleInit() exit\n"));
1348     return;
1349 }
1350 
1351 #ifdef DEBUG_OUT
1352 void DumpInstrStats( void );
1353 #endif
1354 
1355 /* called once per module. AssembleModule() cleanup */
1356 
AssembleFini(void)1357 static void AssembleFini( void )
1358 /******************************/
1359 {
1360     int i;
1361     SegmentFini();
1362     SymFini();
1363     ResWordsFini();
1364 #ifdef DEBUG_OUT
1365     DumpInstrStats();
1366     MacroFini();
1367 #endif
1368     FreePubQueue();
1369 #if FASTMEM==0
1370     FreeLibQueue();
1371     ContextFini();
1372     HllFini();
1373 #endif
1374     InputFini();
1375     close_files();
1376 
1377 #if FASTPASS
1378 #if FASTMEM==0
1379     FreeLineStore();
1380 #endif
1381 #endif
1382 
1383     for ( i = 0; i < NUM_FILE_TYPES; i++ ) {
1384         LclFree( CurrFName[i] );
1385         /* v2.05: make sure the pointer for ERR is cleared */
1386         CurrFName[i] = NULL;
1387     }
1388     MemFini();
1389     return;
1390 }
1391 
1392 /* AssembleModule() assembles one source file */
1393 
AssembleModule(const char * source)1394 int EXPQUAL AssembleModule( const char *source )
1395 /**********************************************/
1396 {
1397     uint_32       prev_written = -1;
1398     uint_32       curr_written;
1399     int           starttime;
1400     int           endtime;
1401     struct dsym   *seg;
1402 
1403     DebugMsg(("AssembleModule(\"%s\") enter\n", source ));
1404 
1405     memset( &ModuleInfo, 0, sizeof(ModuleInfo) );
1406     DebugCmd( ModuleInfo.cref = TRUE ); /* enable debug displays */
1407 
1408 #if 1 //def __SW_BD
1409     /* fatal errors during assembly won't terminate the program,
1410      * just the assembly step.!
1411      */
1412     if ( setjmp( jmpenv ) ) {
1413         if ( ModuleInfo.g.src_stack )
1414             ClearSrcStack(); /* avoid memory leaks! */
1415         goto done;
1416     }
1417 #endif
1418 
1419     AssembleInit( source );
1420 
1421     starttime = clock();
1422 
1423 #if 0 /* 1=trigger a protection fault */
1424     seg = NULL;
1425     seg->sym.state = SYM_UNDEFINED;
1426 #endif
1427 
1428     for( Parse_Pass = PASS_1; ; Parse_Pass++ ) {
1429 
1430         DebugMsg(( "*************\npass %u\n*************\n", Parse_Pass + 1 ));
1431         OnePass();
1432 
1433         if( ModuleInfo.g.error_count > 0 ) {
1434             DebugMsg(("AssembleModule(%u): errorcnt=%u\n", Parse_Pass + 1, ModuleInfo.g.error_count ));
1435             break;
1436         }
1437 
1438         /* calculate total size of segments */
1439         for ( curr_written = 0, seg = SymTables[TAB_SEG].head; seg ; seg = seg->next ) {
1440             /* v2.04: use <max_offset> instead of <bytes_written>
1441              * (the latter is not always reliable due to backpatching).
1442              */
1443             //curr_written += seg->e.seginfo->bytes_written;
1444             curr_written += seg->sym.max_offset;
1445             DebugMsg(("AssembleModule(%u): segm=%-8s start=%8" I32_SPEC "X max_ofs=%8" I32_SPEC "X written=%" I32_SPEC "X\n",
1446                       Parse_Pass + 1, seg->sym.name, seg->e.seginfo->start_loc, seg->sym.max_offset,
1447                       seg->e.seginfo->bytes_written ));
1448         }
1449 
1450         /* if there's no phase error and size of segments didn't change, we're done */
1451         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));
1452         if( !ModuleInfo.PhaseError && prev_written == curr_written )
1453             break;
1454 
1455 #ifdef DEBUG_OUT
1456         if ( curr_written < prev_written && prev_written != -1 ) {
1457             printf( "size shrank from %" I32_SPEC "X to %" I32_SPEC "X in pass %u\n", prev_written, curr_written, Parse_Pass + 1 );
1458         }
1459 #endif
1460 
1461         DebugMsg(("AssembleModule(%u): prepare for next pass\n", Parse_Pass + 1));
1462         prev_written = curr_written;
1463 
1464         if ( Parse_Pass % 200 == 199 )
1465             EmitWarn( 2, ASSEMBLY_PASSES, Parse_Pass+1 );
1466 #ifdef DEBUG_OUT
1467         if ( Options.max_passes && Parse_Pass == (Options.max_passes - 1) )
1468             break;
1469 #endif
1470         if ( Options.line_numbers ) {
1471 #if COFF_SUPPORT
1472             if ( Options.output_format == OFORMAT_COFF ) {
1473                 for( seg = SymTables[TAB_SEG].head; seg; seg = seg->next ) {
1474                     if ( seg->e.seginfo->LinnumQueue )
1475                         QueueDeleteLinnum( seg->e.seginfo->LinnumQueue );
1476                     seg->e.seginfo->LinnumQueue = NULL;
1477                 }
1478             } else {
1479 #endif
1480                 QueueDeleteLinnum( &LinnumQueue );
1481                 LinnumQueue.head = NULL;
1482 #if COFF_SUPPORT
1483             }
1484 #endif
1485         }
1486 
1487         /* set file position of ASM and LST files for next pass */
1488 
1489         rewind( CurrFile[ASM] );
1490         if ( write_to_file && Options.output_format == OFORMAT_OMF )
1491             omf_set_filepos();
1492 
1493 #if FASTPASS
1494         if ( UseSavedState == FALSE && CurrFile[LST] ) {
1495 #else
1496         if ( CurrFile[LST] ) {
1497 #endif
1498             rewind( CurrFile[LST] );
1499             LstInit();
1500         }
1501     } /* end for() */
1502 
1503     if ( ( Parse_Pass > PASS_1 ) && write_to_file )
1504         WriteModule( &ModuleInfo );
1505 
1506     if ( ModuleInfo.pCodeBuff ) {
1507         LclFree( ModuleInfo.pCodeBuff );
1508     }
1509     DebugMsg(("AssembleModule: finished, cleanup\n"));
1510 
1511     /* Write a symbol listing file (if requested) */
1512     LstWriteCRef();
1513 
1514     endtime = clock(); /* is in ms already */
1515 
1516     sprintf( CurrSource, MsgGetEx( MSG_ASSEMBLY_RESULTS ),
1517              GetFName( ModuleInfo.srcfile )->fname,
1518              GetLineNumber(),
1519              Parse_Pass + 1,
1520              endtime - starttime,
1521              ModuleInfo.g.warning_count,
1522              ModuleInfo.g.error_count);
1523     if ( Options.quiet == FALSE )
1524         printf( "%s\n", CurrSource );
1525 
1526     /* v2.13: suppress the final msg (mostly useful for regression tests) */
1527     //if ( CurrFile[LST] ) {
1528     if ( CurrFile[LST] && Options.no_final_msg_listing == FALSE ) {
1529         LstPrintf( CurrSource );
1530         LstNL();
1531     }
1532 #if 1 //def __SW_BD
1533 done:
1534 #endif
1535     AssembleFini();
1536     DebugMsg(("AssembleModule exit\n"));
1537     return( ModuleInfo.g.error_count == 0 );
1538 }
1539