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