1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 #include "../../qcommon/q_platform.h"
24 #include "cmdlib.h"
25 #include "mathlib.h"
26 #include "../../qcommon/qfiles.h"
27 
28 /* 19079 total symbols in FI, 2002 Jan 23 */
29 #define DEFAULT_HASHTABLE_SIZE 2048
30 
31 char	outputFilename[MAX_OS_PATH];
32 
33 // the zero page size is just used for detecting run time faults
34 #define	ZERO_PAGE_SIZE	0		// 256
35 
36 typedef enum {
37 	OP_UNDEF,
38 
39 	OP_IGNORE,
40 
41 	OP_BREAK,
42 
43 	OP_ENTER,
44 	OP_LEAVE,
45 	OP_CALL,
46 	OP_PUSH,
47 	OP_POP,
48 
49 	OP_CONST,
50 	OP_LOCAL,
51 
52 	OP_JUMP,
53 
54 	//-------------------
55 
56 	OP_EQ,
57 	OP_NE,
58 
59 	OP_LTI,
60 	OP_LEI,
61 	OP_GTI,
62 	OP_GEI,
63 
64 	OP_LTU,
65 	OP_LEU,
66 	OP_GTU,
67 	OP_GEU,
68 
69 	OP_EQF,
70 	OP_NEF,
71 
72 	OP_LTF,
73 	OP_LEF,
74 	OP_GTF,
75 	OP_GEF,
76 
77 	//-------------------
78 
79 	OP_LOAD1,
80 	OP_LOAD2,
81 	OP_LOAD4,
82 	OP_STORE1,
83 	OP_STORE2,
84 	OP_STORE4,				// *(stack[top-1]) = stack[yop
85 	OP_ARG,
86 	OP_BLOCK_COPY,
87 
88 	//-------------------
89 
90 	OP_SEX8,
91 	OP_SEX16,
92 
93 	OP_NEGI,
94 	OP_ADD,
95 	OP_SUB,
96 	OP_DIVI,
97 	OP_DIVU,
98 	OP_MODI,
99 	OP_MODU,
100 	OP_MULI,
101 	OP_MULU,
102 
103 	OP_BAND,
104 	OP_BOR,
105 	OP_BXOR,
106 	OP_BCOM,
107 
108 	OP_LSH,
109 	OP_RSHI,
110 	OP_RSHU,
111 
112 	OP_NEGF,
113 	OP_ADDF,
114 	OP_SUBF,
115 	OP_DIVF,
116 	OP_MULF,
117 
118 	OP_CVIF,
119 	OP_CVFI
120 } opcode_t;
121 
122 typedef struct {
123 	int		imageBytes;		// after decompression
124 	int		entryPoint;
125 	int		stackBase;
126 	int		stackSize;
127 } executableHeader_t;
128 
129 typedef enum {
130 	CODESEG,
131 	DATASEG,	// initialized 32 bit data, will be byte swapped
132 	LITSEG,		// strings
133 	BSSSEG,		// 0 filled
134 	JTRGSEG,	// pseudo-segment that contains only jump table targets
135 	NUM_SEGMENTS
136 } segmentName_t;
137 
138 #define	MAX_IMAGE	0x400000
139 
140 typedef struct {
141 	byte	image[MAX_IMAGE];
142 	int		imageUsed;
143 	int		segmentBase;		// only valid on second pass
144 } segment_t;
145 
146 typedef struct symbol_s {
147 	struct	symbol_s	*next;
148 	int		hash;
149 	segment_t	*segment;
150 	char	*name;
151 	int		value;
152 } symbol_t;
153 
154 typedef struct hashchain_s {
155   void *data;
156   struct hashchain_s *next;
157 } hashchain_t;
158 
159 typedef struct hashtable_s {
160   int buckets;
161   hashchain_t **table;
162 } hashtable_t;
163 
164 int symtablelen = DEFAULT_HASHTABLE_SIZE;
165 hashtable_t *symtable;
166 hashtable_t *optable;
167 
168 segment_t	segment[NUM_SEGMENTS];
169 segment_t	*currentSegment;
170 
171 int		passNumber;
172 
173 int		numSymbols;
174 int		errorCount;
175 
176 typedef struct options_s {
177 	qboolean verbose;
178 	qboolean writeMapFile;
179 	qboolean vanillaQ3Compatibility;
180 } options_t;
181 
182 options_t options = { 0 };
183 
184 symbol_t	*symbols;
185 symbol_t	*lastSymbol = 0;  /* Most recent symbol defined. */
186 
187 
188 #define	MAX_ASM_FILES	256
189 int		numAsmFiles;
190 char	*asmFiles[MAX_ASM_FILES];
191 char	*asmFileNames[MAX_ASM_FILES];
192 
193 int		currentFileIndex;
194 char	*currentFileName;
195 int		currentFileLine;
196 
197 //int		stackSize = 16384;
198 int		stackSize = 0x10000;
199 
200 // we need to convert arg and ret instructions to
201 // stores to the local stack frame, so we need to track the
202 // characteristics of the current functions stack frame
203 int		currentLocals;			// bytes of locals needed by this function
204 int		currentArgs;			// bytes of largest argument list called from this function
205 int		currentArgOffset;		// byte offset in currentArgs to store next arg, reset each call
206 
207 #define	MAX_LINE_LENGTH	1024
208 char	lineBuffer[MAX_LINE_LENGTH];
209 int		lineParseOffset;
210 char	token[MAX_LINE_LENGTH];
211 
212 int		instructionCount;
213 
214 typedef struct {
215 	char	*name;
216 	int		opcode;
217 } sourceOps_t;
218 
219 sourceOps_t		sourceOps[] = {
220 #include "opstrings.h"
221 };
222 
223 #define	NUM_SOURCE_OPS ( sizeof( sourceOps ) / sizeof( sourceOps[0] ) )
224 
225 int		opcodesHash[ NUM_SOURCE_OPS ];
226 
227 
228 
vreport(const char * fmt,va_list vp)229 static int vreport (const char* fmt, va_list vp)
230 {
231   if (options.verbose != qtrue)
232       return 0;
233   return vprintf(fmt, vp);
234 }
235 
report(const char * fmt,...)236 static int report (const char *fmt, ...)
237 {
238   va_list va;
239   int retval;
240 
241   va_start(va, fmt);
242   retval = vreport(fmt, va);
243   va_end(va);
244   return retval;
245 }
246 
247 /* The chain-and-bucket hash table.  -PH */
248 
hashtable_init(hashtable_t * H,int buckets)249 static void hashtable_init (hashtable_t *H, int buckets)
250 {
251   H->buckets = buckets;
252   H->table = calloc(H->buckets, sizeof(*(H->table)));
253   return;
254 }
255 
hashtable_new(int buckets)256 static hashtable_t *hashtable_new (int buckets)
257 {
258   hashtable_t *H;
259 
260   H = malloc(sizeof(hashtable_t));
261   hashtable_init(H, buckets);
262   return H;
263 }
264 
265 /* No destroy/destructor.  No need. */
266 
hashtable_add(hashtable_t * H,int hashvalue,void * datum)267 static void hashtable_add (hashtable_t *H, int hashvalue, void *datum)
268 {
269   hashchain_t *hc, **hb;
270 
271   hashvalue = (abs(hashvalue) % H->buckets);
272   hb = &(H->table[hashvalue]);
273   if (*hb == 0)
274     {
275       /* Empty bucket.  Create new one. */
276       *hb = calloc(1, sizeof(**hb));
277       hc = *hb;
278     }
279   else
280     {
281       /* Get hc to point to last node in chain. */
282       for (hc = *hb; hc && hc->next; hc = hc->next);
283       hc->next = calloc(1, sizeof(*hc));
284       hc = hc->next;
285     }
286   hc->data = datum;
287   hc->next = 0;
288   return;
289 }
290 
hashtable_get(hashtable_t * H,int hashvalue)291 static hashchain_t *hashtable_get (hashtable_t *H, int hashvalue)
292 {
293   hashvalue = (abs(hashvalue) % H->buckets);
294   return (H->table[hashvalue]);
295 }
296 
hashtable_stats(hashtable_t * H)297 static void hashtable_stats (hashtable_t *H)
298 {
299   int len, empties, longest, nodes;
300   int i;
301   float meanlen;
302   hashchain_t *hc;
303 
304   report("Stats for hashtable %08X", H);
305   empties = 0;
306   longest = 0;
307   nodes = 0;
308   for (i = 0; i < H->buckets; i++)
309     {
310       if (H->table[i] == 0)
311         { empties++; continue; }
312       for (hc = H->table[i], len = 0; hc; hc = hc->next, len++);
313       if (len > longest) { longest = len; }
314       nodes += len;
315     }
316   meanlen = (float)(nodes) / (H->buckets - empties);
317 #if 0
318 /* Long stats display */
319   report(" Total buckets: %d\n", H->buckets);
320   report(" Total stored nodes: %d\n", nodes);
321   report(" Longest chain: %d\n", longest);
322   report(" Empty chains: %d\n", empties);
323   report(" Mean non-empty chain length: %f\n", meanlen);
324 #else //0
325 /* Short stats display */
326   report(", %d buckets, %d nodes", H->buckets, nodes);
327   report("\n");
328   report(" Longest chain: %d, empty chains: %d, mean non-empty: %f", longest, empties, meanlen);
329 #endif //0
330   report("\n");
331 }
332 
333 
334 /* Kludge. */
335 /* Check if symbol already exists. */
336 /* Returns 0 if symbol does NOT already exist, non-zero otherwise. */
hashtable_symbol_exists(hashtable_t * H,int hash,char * sym)337 static int hashtable_symbol_exists (hashtable_t *H, int hash, char *sym)
338 {
339   hashchain_t *hc;
340   symbol_t *s;
341 
342   hash = (abs(hash) % H->buckets);
343   hc = H->table[hash];
344   if (hc == 0)
345     {
346       /* Empty chain means this symbol has not yet been defined. */
347       return 0;
348     }
349   for (; hc; hc = hc->next)
350     {
351       s = (symbol_t*)hc->data;
352 //      if ((hash == s->hash) && (strcmp(sym, s->name) == 0))
353 /* We _already_ know the hash is the same.  That's why we're probing! */
354       if (strcmp(sym, s->name) == 0)
355         {
356           /* Symbol collisions -- symbol already exists. */
357           return 1;
358         }
359     }
360   return 0;  /* Can't find collision. */
361 }
362 
363 
364 
365 
366 /* Comparator function for quicksorting. */
symlist_cmp(const void * e1,const void * e2)367 static int symlist_cmp (const void *e1, const void *e2)
368 {
369   const symbol_t *a, *b;
370 
371   a = *(const symbol_t **)e1;
372   b = *(const symbol_t **)e2;
373 //crumb("Symbol comparison (1) %d  to  (2) %d\n", a->value, b->value);
374   return ( a->value - b->value);
375 }
376 
377 /*
378   Sort the symbols list by using QuickSort (qsort()).
379   This may take a LOT of memory (a few megabytes?), but memory is cheap these days.
380   However, qsort(3) already exists, and I'm really lazy.
381  -PH
382 */
sort_symbols()383 static void sort_symbols ()
384 {
385   int i, elems;
386   symbol_t *s;
387   symbol_t **symlist;
388 
389 //crumb("sort_symbols: Constructing symlist array\n");
390   for (elems = 0, s = symbols; s; s = s->next, elems++) /* nop */ ;
391   symlist = malloc(elems * sizeof(symbol_t*));
392   for (i = 0, s = symbols; s; s = s->next, i++)
393     {
394       symlist[i] = s;
395     }
396 //crumbf("sort_symbols: Quick-sorting %d symbols\n", elems);
397   qsort(symlist, elems, sizeof(symbol_t*), symlist_cmp);
398 //crumbf("sort_symbols: Reconstructing symbols list\n");
399   s = symbols = symlist[0];
400   for (i = 1; i < elems; i++)
401     {
402       s->next = symlist[i];
403       s = s->next;
404     }
405   lastSymbol = s;
406   s->next = 0;
407 //crumbf("sort_symbols: verifying..."); fflush(stdout);
408   for (i = 0, s = symbols; s; s = s->next, i++) /*nop*/ ;
409 //crumbf(" %d elements\n", i);
410   free(symlist);  /* d'oh.  no gc. */
411 }
412 
413 
414 #ifdef _MSC_VER
415 #define INT64 __int64
416 #define atoi64 _atoi64
417 #else
418 #define INT64 long long int
419 #define atoi64 atoll
420 #endif
421 
422 /*
423  Problem:
424 	BYTE values are specified as signed decimal string.  A properly functional
425 	atoip() will cap large signed values at 0x7FFFFFFF.  Negative word values are
426 	often specified as very large decimal values by lcc.  Therefore, values that
427 	should be between 0x7FFFFFFF and 0xFFFFFFFF come out as 0x7FFFFFFF when using
428 	atoi().  Bad.
429 
430  This function is one big evil hack to work around this problem.
431 */
atoiNoCap(const char * s)432 static int atoiNoCap (const char *s)
433 {
434   INT64 l;
435   union {
436     unsigned int u;
437     signed int i;
438   } retval;
439 
440   l = atoi64(s);
441   /* Now smash to signed 32 bits accordingly. */
442   if (l < 0) {
443     retval.i = (int)l;
444   } else {
445     retval.u = (unsigned int)l;
446   }
447   return retval.i;  /* <- union hackage.  I feel dirty with this.  -PH */
448 }
449 
450 
451 
452 /*
453 =============
454 HashString
455 =============
456 */
457 /* Default hash function of Kazlib 1.19, slightly modified. */
HashString(const char * key)458 static unsigned int HashString (const char *key)
459 {
460     static unsigned long randbox[] = {
461     0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
462     0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
463     0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
464     0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
465     };
466 
467     const char *str = key;
468     unsigned int acc = 0;
469 
470     while (*str) {
471     acc ^= randbox[(*str + acc) & 0xf];
472     acc = (acc << 1) | (acc >> 31);
473     acc &= 0xffffffffU;
474     acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
475     acc = (acc << 2) | (acc >> 30);
476     acc &= 0xffffffffU;
477     }
478     return abs(acc);
479 }
480 
481 
482 /*
483 ============
484 CodeError
485 ============
486 */
CodeError(char * fmt,...)487 static void CodeError( char *fmt, ... ) {
488 	va_list		argptr;
489 
490 	errorCount++;
491 
492 	fprintf( stderr, "%s:%i ", currentFileName, currentFileLine );
493 
494 	va_start( argptr,fmt );
495 	vfprintf( stderr, fmt, argptr );
496 	va_end( argptr );
497 }
498 
499 /*
500 ============
501 EmitByte
502 ============
503 */
EmitByte(segment_t * seg,int v)504 static void EmitByte( segment_t *seg, int v ) {
505 	if ( seg->imageUsed >= MAX_IMAGE ) {
506 		Error( "MAX_IMAGE" );
507 	}
508 	seg->image[ seg->imageUsed ] = v;
509 	seg->imageUsed++;
510 }
511 
512 /*
513 ============
514 EmitInt
515 ============
516 */
EmitInt(segment_t * seg,int v)517 static void EmitInt( segment_t *seg, int v ) {
518 	if ( seg->imageUsed >= MAX_IMAGE - 4) {
519 		Error( "MAX_IMAGE" );
520 	}
521 	seg->image[ seg->imageUsed ] = v & 255;
522 	seg->image[ seg->imageUsed + 1 ] = ( v >> 8 ) & 255;
523 	seg->image[ seg->imageUsed + 2 ] = ( v >> 16 ) & 255;
524 	seg->image[ seg->imageUsed + 3 ] = ( v >> 24 ) & 255;
525 	seg->imageUsed += 4;
526 }
527 
528 /*
529 ============
530 DefineSymbol
531 
532 Symbols can only be defined on pass 0
533 ============
534 */
DefineSymbol(char * sym,int value)535 static void DefineSymbol( char *sym, int value ) {
536 	/* Hand optimization by PhaethonH */
537 	symbol_t	*s;
538 	char		expanded[MAX_LINE_LENGTH];
539 	int			hash;
540 
541 	if ( passNumber == 1 ) {
542 		return;
543 	}
544 
545 	// add the file prefix to local symbols to guarantee unique
546 	if ( sym[0] == '$' ) {
547 		sprintf( expanded, "%s_%i", sym, currentFileIndex );
548 		sym = expanded;
549 	}
550 
551 	hash = HashString( sym );
552 
553 	if (hashtable_symbol_exists(symtable, hash, sym)) {
554 		CodeError( "Multiple definitions for %s\n", sym );
555 		return;
556 	}
557 
558 	s = malloc( sizeof( *s ) );
559 	s->next = NULL;
560 	s->name = copystring( sym );
561 	s->hash = hash;
562 	s->value = value;
563 	s->segment = currentSegment;
564 
565 	hashtable_add(symtable, hash, s);
566 
567 /*
568   Hash table lookup already speeds up symbol lookup enormously.
569   We postpone sorting until end of pass 0.
570   Since we're not doing the insertion sort, lastSymbol should always
571    wind up pointing to the end of list.
572   This allows constant time for adding to the list.
573  -PH
574 */
575 	if (symbols == 0) {
576 		lastSymbol = symbols = s;
577 	} else {
578 		lastSymbol->next = s;
579 		lastSymbol = s;
580 	}
581 }
582 
583 
584 /*
585 ============
586 LookupSymbol
587 
588 Symbols can only be evaluated on pass 1
589 ============
590 */
LookupSymbol(char * sym)591 static int LookupSymbol( char *sym ) {
592 	symbol_t	*s;
593 	char		expanded[MAX_LINE_LENGTH];
594 	int			hash;
595 	hashchain_t *hc;
596 
597 	if ( passNumber == 0 ) {
598 		return 0;
599 	}
600 
601 	// add the file prefix to local symbols to guarantee unique
602 	if ( sym[0] == '$' ) {
603 		sprintf( expanded, "%s_%i", sym, currentFileIndex );
604 		sym = expanded;
605 	}
606 
607 	hash = HashString( sym );
608 
609 /*
610   Hand optimization by PhaethonH
611 
612   Using a hash table with chain/bucket for lookups alone sped up q3asm by almost 3x for me.
613  -PH
614 */
615 	for (hc = hashtable_get(symtable, hash); hc; hc = hc->next) {
616 		s = (symbol_t*)hc->data;  /* ugly typecasting, but it's fast! */
617 		if ( (hash == s->hash) && !strcmp(sym, s->name) ) {
618 			return s->segment->segmentBase + s->value;
619 		}
620 	}
621 
622 	CodeError( "error: symbol %s undefined\n", sym );
623 	passNumber = 0;
624 	DefineSymbol( sym, 0 );	// so more errors aren't printed
625 	passNumber = 1;
626 	return 0;
627 }
628 
629 
630 /*
631 ==============
632 ExtractLine
633 
634 Extracts the next line from the given text block.
635 If a full line isn't parsed, returns NULL
636 Otherwise returns the updated parse pointer
637 ===============
638 */
ExtractLine(char * data)639 static char *ExtractLine( char *data ) {
640 /* Goal:
641 	 Given a string `data', extract one text line into buffer `lineBuffer' that
642 	 is no longer than MAX_LINE_LENGTH characters long.  Return value is
643 	 remainder of `data' that isn't part of `lineBuffer'.
644  -PH
645 */
646 	/* Hand-optimized by PhaethonH */
647 	char 	*p, *q;
648 
649 	currentFileLine++;
650 
651 	lineParseOffset = 0;
652 	token[0] = 0;
653 	*lineBuffer = 0;
654 
655 	p = q = data;
656 	if (!*q) {
657 		return NULL;
658 	}
659 
660 	for ( ; !((*p == 0) || (*p == '\n')); p++)  /* nop */ ;
661 
662 	if ((p - q) >= MAX_LINE_LENGTH) {
663 		CodeError( "MAX_LINE_LENGTH" );
664 		return data;
665 	}
666 
667 	memcpy( lineBuffer, data, (p - data) );
668 	lineBuffer[(p - data)] = 0;
669 	p += (*p == '\n') ? 1 : 0;  /* Skip over final newline. */
670 	return p;
671 }
672 
673 
674 /*
675 ==============
676 Parse
677 
678 Parse a token out of linebuffer
679 ==============
680 */
Parse(void)681 static qboolean Parse( void ) {
682 	/* Hand-optimized by PhaethonH */
683 	const char 	*p, *q;
684 
685 	/* Because lineParseOffset is only updated just before exit, this makes this code version somewhat harder to debug under a symbolic debugger. */
686 
687 	*token = 0;  /* Clear token. */
688 
689 	// skip whitespace
690 	for (p = lineBuffer + lineParseOffset; *p && (*p <= ' '); p++) /* nop */ ;
691 
692 	// skip ; comments
693 	/* die on end-of-string */
694 	if ((*p == ';') || (*p == 0)) {
695 		lineParseOffset = p - lineBuffer;
696 		return qfalse;
697 	}
698 
699 	q = p;  /* Mark the start of token. */
700 	/* Find separator first. */
701 	for ( ; *p > 32; p++) /* nop */ ;  /* XXX: unsafe assumptions. */
702 	/* *p now sits on separator.  Mangle other values accordingly. */
703 	strncpy(token, q, p - q);
704 	token[p - q] = 0;
705 
706 	lineParseOffset = p - lineBuffer;
707 
708 	return qtrue;
709 }
710 
711 
712 /*
713 ==============
714 ParseValue
715 ==============
716 */
ParseValue(void)717 static int ParseValue( void ) {
718 	Parse();
719 	return atoiNoCap( token );
720 }
721 
722 
723 /*
724 ==============
725 ParseExpression
726 ==============
727 */
ParseExpression(void)728 static int ParseExpression(void) {
729 	/* Hand optimization, PhaethonH */
730 	int		i, j;
731 	char	sym[MAX_LINE_LENGTH];
732 	int		v;
733 
734 	/* Skip over a leading minus. */
735 	for ( i = ((token[0] == '-') ? 1 : 0) ; i < MAX_LINE_LENGTH ; i++ ) {
736 		if ( token[i] == '+' || token[i] == '-' || token[i] == 0 ) {
737 			break;
738 		}
739 	}
740 
741 	memcpy( sym, token, i );
742 	sym[i] = 0;
743 
744 	switch (*sym) {  /* Resolve depending on first character. */
745 /* Optimizing compilers can convert cases into "calculated jumps".  I think these are faster.  -PH */
746 		case '-':
747 		case '0': case '1': case '2': case '3': case '4':
748 		case '5': case '6': case '7': case '8': case '9':
749 			v = atoiNoCap(sym);
750 			break;
751 		default:
752 			v = LookupSymbol(sym);
753 			break;
754 	}
755 
756 	// parse add / subtract offsets
757 	while ( token[i] != 0 ) {
758 		for ( j = i + 1 ; j < MAX_LINE_LENGTH ; j++ ) {
759 			if ( token[j] == '+' || token[j] == '-' || token[j] == 0 ) {
760 				break;
761 			}
762 		}
763 
764 		memcpy( sym, token+i+1, j-i-1 );
765 		sym[j-i-1] = 0;
766 
767 		switch (token[i]) {
768 			case '+':
769 				v += atoiNoCap(sym);
770 				break;
771 			case '-':
772 				v -= atoiNoCap(sym);
773 				break;
774 		}
775 
776 		i = j;
777 	}
778 
779 	return v;
780 }
781 
782 
783 /*
784 ==============
785 HackToSegment
786 
787 BIG HACK: I want to put all 32 bit values in the data
788 segment so they can be byte swapped, and all char data in the lit
789 segment, but switch jump tables are emited in the lit segment and
790 initialized strng variables are put in the data segment.
791 
792 I can change segments here, but I also need to fixup the
793 label that was just defined
794 
795 Note that the lit segment is read-write in the VM, so strings
796 aren't read only as in some architectures.
797 ==============
798 */
HackToSegment(segmentName_t seg)799 static void HackToSegment( segmentName_t seg ) {
800 	if ( currentSegment == &segment[seg] ) {
801 		return;
802 	}
803 
804 	currentSegment = &segment[seg];
805 	if ( passNumber == 0 ) {
806 		lastSymbol->segment = currentSegment;
807 		lastSymbol->value = currentSegment->imageUsed;
808 	}
809 }
810 
811 
812 
813 
814 
815 
816 
817 //#define STAT(L) report("STAT " L "\n");
818 #define STAT(L)
819 #define ASM(O) int TryAssemble##O ()
820 
821 
822 /*
823   These clauses were moved out from AssembleLine() to allow reordering of if's.
824   An optimizing compiler should reconstruct these back into inline code.
825  -PH
826 */
827 
828 	// call instructions reset currentArgOffset
ASM(CALL)829 ASM(CALL)
830 {
831 	if ( !strncmp( token, "CALL", 4 ) ) {
832 STAT("CALL");
833 		EmitByte( &segment[CODESEG], OP_CALL );
834 		instructionCount++;
835 		currentArgOffset = 0;
836 		return 1;
837 	}
838 	return 0;
839 }
840 
841 	// arg is converted to a reversed store
ASM(ARG)842 ASM(ARG)
843 {
844 	if ( !strncmp( token, "ARG", 3 ) ) {
845 STAT("ARG");
846 		EmitByte( &segment[CODESEG], OP_ARG );
847 		instructionCount++;
848 		if ( 8 + currentArgOffset >= 256 ) {
849 			CodeError( "currentArgOffset >= 256" );
850 			return 1;
851 		}
852 		EmitByte( &segment[CODESEG], 8 + currentArgOffset );
853 		currentArgOffset += 4;
854 		return 1;
855 	}
856 	return 0;
857 }
858 
859 	// ret just leaves something on the op stack
ASM(RET)860 ASM(RET)
861 {
862 	if ( !strncmp( token, "RET", 3 ) ) {
863 STAT("RET");
864 		EmitByte( &segment[CODESEG], OP_LEAVE );
865 		instructionCount++;
866 		EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
867 		return 1;
868 	}
869 	return 0;
870 }
871 
872 	// pop is needed to discard the return value of
873 	// a function
ASM(POP)874 ASM(POP)
875 {
876 	if ( !strncmp( token, "pop", 3 ) ) {
877 STAT("POP");
878 		EmitByte( &segment[CODESEG], OP_POP );
879 		instructionCount++;
880 		return 1;
881 	}
882 	return 0;
883 }
884 
885 	// address of a parameter is converted to OP_LOCAL
ASM(ADDRF)886 ASM(ADDRF)
887 {
888 	int		v;
889 	if ( !strncmp( token, "ADDRF", 5 ) ) {
890 STAT("ADDRF");
891 		instructionCount++;
892 		Parse();
893 		v = ParseExpression();
894 		v = 16 + currentArgs + currentLocals + v;
895 		EmitByte( &segment[CODESEG], OP_LOCAL );
896 		EmitInt( &segment[CODESEG], v );
897 		return 1;
898 	}
899 	return 0;
900 }
901 
902 	// address of a local is converted to OP_LOCAL
ASM(ADDRL)903 ASM(ADDRL)
904 {
905 	int		v;
906 	if ( !strncmp( token, "ADDRL", 5 ) ) {
907 STAT("ADDRL");
908 		instructionCount++;
909 		Parse();
910 		v = ParseExpression();
911 		v = 8 + currentArgs + v;
912 		EmitByte( &segment[CODESEG], OP_LOCAL );
913 		EmitInt( &segment[CODESEG], v );
914 		return 1;
915 	}
916 	return 0;
917 }
918 
ASM(PROC)919 ASM(PROC)
920 {
921 	char	name[1024];
922 	if ( !strcmp( token, "proc" ) ) {
923 STAT("PROC");
924 		Parse();					// function name
925 		strcpy( name, token );
926 
927 		DefineSymbol( token, instructionCount ); // segment[CODESEG].imageUsed );
928 
929 		currentLocals = ParseValue();	// locals
930 		currentLocals = ( currentLocals + 3 ) & ~3;
931 		currentArgs = ParseValue();		// arg marshalling
932 		currentArgs = ( currentArgs + 3 ) & ~3;
933 
934 		if ( 8 + currentLocals + currentArgs >= 32767 ) {
935 			CodeError( "Locals > 32k in %s\n", name );
936 		}
937 
938 		instructionCount++;
939 		EmitByte( &segment[CODESEG], OP_ENTER );
940 		EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
941 		return 1;
942 	}
943 	return 0;
944 }
945 
946 
ASM(ENDPROC)947 ASM(ENDPROC)
948 {
949 	int		v, v2;
950 	if ( !strcmp( token, "endproc" ) ) {
951 STAT("ENDPROC");
952 		Parse();				// skip the function name
953 		v = ParseValue();		// locals
954 		v2 = ParseValue();		// arg marshalling
955 
956 		// all functions must leave something on the opstack
957 		instructionCount++;
958 		EmitByte( &segment[CODESEG], OP_PUSH );
959 
960 		instructionCount++;
961 		EmitByte( &segment[CODESEG], OP_LEAVE );
962 		EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
963 
964 		return 1;
965 	}
966 	return 0;
967 }
968 
969 
ASM(ADDRESS)970 ASM(ADDRESS)
971 {
972 	int		v;
973 	if ( !strcmp( token, "address" ) ) {
974 STAT("ADDRESS");
975 		Parse();
976 		v = ParseExpression();
977 
978 /* Addresses are 32 bits wide, and therefore go into data segment. */
979 		HackToSegment( DATASEG );
980 		EmitInt( currentSegment, v );
981 		if( passNumber == 1 && token[ 0 ] == '$' ) // crude test for labels
982 			EmitInt( &segment[ JTRGSEG ], v );
983 		return 1;
984 	}
985 	return 0;
986 }
987 
ASM(EXPORT)988 ASM(EXPORT)
989 {
990 	if ( !strcmp( token, "export" ) ) {
991 STAT("EXPORT");
992 		return 1;
993 	}
994 	return 0;
995 }
996 
ASM(IMPORT)997 ASM(IMPORT)
998 {
999 	if ( !strcmp( token, "import" ) ) {
1000 STAT("IMPORT");
1001 		return 1;
1002 	}
1003 	return 0;
1004 }
1005 
ASM(CODE)1006 ASM(CODE)
1007 {
1008 	if ( !strcmp( token, "code" ) ) {
1009 STAT("CODE");
1010 		currentSegment = &segment[CODESEG];
1011 		return 1;
1012 	}
1013 	return 0;
1014 }
1015 
ASM(BSS)1016 ASM(BSS)
1017 {
1018 	if ( !strcmp( token, "bss" ) ) {
1019 STAT("BSS");
1020 		currentSegment = &segment[BSSSEG];
1021 		return 1;
1022 	}
1023 	return 0;
1024 }
1025 
ASM(DATA)1026 ASM(DATA)
1027 {
1028 	if ( !strcmp( token, "data" ) ) {
1029 STAT("DATA");
1030 		currentSegment = &segment[DATASEG];
1031 		return 1;
1032 	}
1033 	return 0;
1034 }
1035 
ASM(LIT)1036 ASM(LIT)
1037 {
1038 	if ( !strcmp( token, "lit" ) ) {
1039 STAT("LIT");
1040 		currentSegment = &segment[LITSEG];
1041 		return 1;
1042 	}
1043 	return 0;
1044 }
1045 
ASM(LINE)1046 ASM(LINE)
1047 {
1048 	if ( !strcmp( token, "line" ) ) {
1049 STAT("LINE");
1050 		return 1;
1051 	}
1052 	return 0;
1053 }
1054 
ASM(FILE)1055 ASM(FILE)
1056 {
1057 	if ( !strcmp( token, "file" ) ) {
1058 STAT("FILE");
1059 		return 1;
1060 	}
1061 	return 0;
1062 }
1063 
ASM(EQU)1064 ASM(EQU)
1065 {
1066 	char	name[1024];
1067 	if ( !strcmp( token, "equ" ) ) {
1068 STAT("EQU");
1069 		Parse();
1070 		strcpy( name, token );
1071 		Parse();
1072 		DefineSymbol( name, atoiNoCap(token) );
1073 		return 1;
1074 	}
1075 	return 0;
1076 }
1077 
ASM(ALIGN)1078 ASM(ALIGN)
1079 {
1080 	int		v;
1081 	if ( !strcmp( token, "align" ) ) {
1082 STAT("ALIGN");
1083 		v = ParseValue();
1084 		currentSegment->imageUsed = (currentSegment->imageUsed + v - 1 ) & ~( v - 1 );
1085 		return 1;
1086 	}
1087 	return 0;
1088 }
1089 
ASM(SKIP)1090 ASM(SKIP)
1091 {
1092 	int		v;
1093 	if ( !strcmp( token, "skip" ) ) {
1094 STAT("SKIP");
1095 		v = ParseValue();
1096 		currentSegment->imageUsed += v;
1097 		return 1;
1098 	}
1099 	return 0;
1100 }
1101 
ASM(BYTE)1102 ASM(BYTE)
1103 {
1104 	int		i, v, v2;
1105 	if ( !strcmp( token, "byte" ) ) {
1106 STAT("BYTE");
1107 		v = ParseValue();
1108 		v2 = ParseValue();
1109 
1110 		if ( v == 1 ) {
1111 /* Character (1-byte) values go into lit(eral) segment. */
1112 			HackToSegment( LITSEG );
1113 		} else if ( v == 4 ) {
1114 /* 32-bit (4-byte) values go into data segment. */
1115 			HackToSegment( DATASEG );
1116 		} else if ( v == 2 ) {
1117 /* and 16-bit (2-byte) values will cause q3asm to barf. */
1118 			CodeError( "16 bit initialized data not supported" );
1119 		}
1120 
1121 		// emit little endien
1122 		for ( i = 0 ; i < v ; i++ ) {
1123 			EmitByte( currentSegment, (v2 & 0xFF) ); /* paranoid ANDing  -PH */
1124 			v2 >>= 8;
1125 		}
1126 		return 1;
1127 	}
1128 	return 0;
1129 }
1130 
1131 	// code labels are emited as instruction counts, not byte offsets,
1132 	// because the physical size of the code will change with
1133 	// different run time compilers and we want to minimize the
1134 	// size of the required translation table
ASM(LABEL)1135 ASM(LABEL)
1136 {
1137 	if ( !strncmp( token, "LABEL", 5 ) ) {
1138 STAT("LABEL");
1139 		Parse();
1140 		if ( currentSegment == &segment[CODESEG] ) {
1141 			DefineSymbol( token, instructionCount );
1142 		} else {
1143 			DefineSymbol( token, currentSegment->imageUsed );
1144 		}
1145 		return 1;
1146 	}
1147 	return 0;
1148 }
1149 
1150 
1151 
1152 /*
1153 ==============
1154 AssembleLine
1155 
1156 ==============
1157 */
AssembleLine(void)1158 static void AssembleLine( void ) {
1159 	hashchain_t *hc;
1160 	sourceOps_t *op;
1161 	int		i;
1162 	int		hash;
1163 
1164 	Parse();
1165 	if ( !token[0] ) {
1166 		return;
1167 	}
1168 
1169 	hash = HashString( token );
1170 
1171 /*
1172   Opcode search using hash table.
1173   Since the opcodes stays mostly fixed, this may benefit even more from a tree.
1174   Always with the tree :)
1175  -PH
1176 */
1177 	for (hc = hashtable_get(optable, hash); hc; hc = hc->next) {
1178 		op = (sourceOps_t*)(hc->data);
1179 		i = op - sourceOps;
1180 		if ((hash == opcodesHash[i]) && (!strcmp(token, op->name))) {
1181 			int		opcode;
1182 			int		expression;
1183 
1184 			if ( op->opcode == OP_UNDEF ) {
1185 				CodeError( "Undefined opcode: %s\n", token );
1186 			}
1187 			if ( op->opcode == OP_IGNORE ) {
1188 				return;		// we ignore most conversions
1189 			}
1190 
1191 			// sign extensions need to check next parm
1192 			opcode = op->opcode;
1193 			if ( opcode == OP_SEX8 ) {
1194 				Parse();
1195 				if ( token[0] == '1' ) {
1196 					opcode = OP_SEX8;
1197 				} else if ( token[0] == '2' ) {
1198 					opcode = OP_SEX16;
1199 				} else {
1200 					CodeError( "Bad sign extension: %s\n", token );
1201 					return;
1202 				}
1203 			}
1204 
1205 			// check for expression
1206 			Parse();
1207 			if ( token[0] && op->opcode != OP_CVIF
1208 					&& op->opcode != OP_CVFI ) {
1209 				expression = ParseExpression();
1210 
1211 				// code like this can generate non-dword block copies:
1212 				// auto char buf[2] = " ";
1213 				// we are just going to round up.  This might conceivably
1214 				// be incorrect if other initialized chars follow.
1215 				if ( opcode == OP_BLOCK_COPY ) {
1216 					expression = ( expression + 3 ) & ~3;
1217 				}
1218 
1219 				EmitByte( &segment[CODESEG], opcode );
1220 				EmitInt( &segment[CODESEG], expression );
1221 			} else {
1222 				EmitByte( &segment[CODESEG], opcode );
1223 			}
1224 
1225 			instructionCount++;
1226 			return;
1227 		}
1228 	}
1229 
1230 /* This falls through if an assembly opcode is not found.  -PH */
1231 
1232 /* The following should be sorted in sequence of statistical frequency, most frequent first.  -PH */
1233 /*
1234 Empirical frequency statistics from FI 2001.01.23:
1235  109892	STAT ADDRL
1236   72188	STAT BYTE
1237   51150	STAT LINE
1238   50906	STAT ARG
1239   43704	STAT IMPORT
1240   34902	STAT LABEL
1241   32066	STAT ADDRF
1242   23704	STAT CALL
1243    7720	STAT POP
1244    7256	STAT RET
1245    5198	STAT ALIGN
1246    3292	STAT EXPORT
1247    2878	STAT PROC
1248    2878	STAT ENDPROC
1249    2812	STAT ADDRESS
1250     738	STAT SKIP
1251     374	STAT EQU
1252     280	STAT CODE
1253     176	STAT LIT
1254     102	STAT FILE
1255     100	STAT BSS
1256      68	STAT DATA
1257 
1258  -PH
1259 */
1260 
1261 #undef ASM
1262 #define ASM(O) if (TryAssemble##O ()) return;
1263 
1264 	ASM(ADDRL)
1265 	ASM(BYTE)
1266 	ASM(LINE)
1267 	ASM(ARG)
1268 	ASM(IMPORT)
1269 	ASM(LABEL)
1270 	ASM(ADDRF)
1271 	ASM(CALL)
1272 	ASM(POP)
1273 	ASM(RET)
1274 	ASM(ALIGN)
1275 	ASM(EXPORT)
1276 	ASM(PROC)
1277 	ASM(ENDPROC)
1278 	ASM(ADDRESS)
1279 	ASM(SKIP)
1280 	ASM(EQU)
1281 	ASM(CODE)
1282 	ASM(LIT)
1283 	ASM(FILE)
1284 	ASM(BSS)
1285 	ASM(DATA)
1286 
1287 	CodeError( "Unknown token: %s\n", token );
1288 }
1289 
1290 /*
1291 ==============
1292 InitTables
1293 ==============
1294 */
InitTables(void)1295 void InitTables( void ) {
1296 	int i;
1297 
1298 	symtable = hashtable_new(symtablelen);
1299 	optable = hashtable_new(100);  /* There's hardly 100 opcodes anyway. */
1300 
1301 	for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) {
1302 		opcodesHash[i] = HashString( sourceOps[i].name );
1303 		hashtable_add(optable, opcodesHash[i], sourceOps + i);
1304 	}
1305 }
1306 
1307 
1308 /*
1309 ==============
1310 WriteMapFile
1311 ==============
1312 */
WriteMapFile(void)1313 static void WriteMapFile( void ) {
1314 	FILE		*f;
1315 	symbol_t	*s;
1316 	char		imageName[MAX_OS_PATH];
1317 	int			seg;
1318 
1319 	strcpy( imageName, outputFilename );
1320 	StripExtension( imageName );
1321 	strcat( imageName, ".map" );
1322 
1323 	report( "Writing %s...\n", imageName );
1324 
1325 	f = SafeOpenWrite( imageName );
1326 	for ( seg = CODESEG ; seg <= BSSSEG ; seg++ ) {
1327 		for ( s = symbols ; s ; s = s->next ) {
1328 			if ( s->name[0] == '$' ) {
1329 				continue;	// skip locals
1330 			}
1331 			if ( &segment[seg] != s->segment ) {
1332 				continue;
1333 			}
1334 			fprintf( f, "%i %8x %s\n", seg, s->value, s->name );
1335 		}
1336 	}
1337 	fclose( f );
1338 }
1339 
1340 /*
1341 ===============
1342 WriteVmFile
1343 ===============
1344 */
WriteVmFile(void)1345 static void WriteVmFile( void ) {
1346 	char	imageName[MAX_OS_PATH];
1347 	vmHeader_t	header;
1348 	FILE	*f;
1349 	int		headerSize;
1350 
1351 	report( "%i total errors\n", errorCount );
1352 
1353 	strcpy( imageName, outputFilename );
1354 	StripExtension( imageName );
1355 	strcat( imageName, ".qvm" );
1356 
1357 	remove( imageName );
1358 
1359 	report( "code segment: %7i\n", segment[CODESEG].imageUsed );
1360 	report( "data segment: %7i\n", segment[DATASEG].imageUsed );
1361 	report( "lit  segment: %7i\n", segment[LITSEG].imageUsed );
1362 	report( "bss  segment: %7i\n", segment[BSSSEG].imageUsed );
1363 	report( "instruction count: %i\n", instructionCount );
1364 
1365 	if ( errorCount != 0 ) {
1366 		report( "Not writing a file due to errors\n" );
1367 		return;
1368 	}
1369 
1370 	if( !options.vanillaQ3Compatibility ) {
1371 		header.vmMagic = VM_MAGIC_VER2;
1372 		headerSize = sizeof( header );
1373 	} else {
1374 		header.vmMagic = VM_MAGIC;
1375 
1376 		// Don't write the VM_MAGIC_VER2 bits when maintaining 1.32b compatibility.
1377 		// (I know this isn't strictly correct due to padding, but then platforms
1378 		// that pad wouldn't be able to write a correct header anyway).  Note: if
1379 		// vmHeader_t changes, this needs to be adjusted too.
1380 		headerSize = sizeof( header ) - sizeof( header.jtrgLength );
1381 	}
1382 
1383 	header.instructionCount = instructionCount;
1384 	header.codeOffset = headerSize;
1385 	header.codeLength = segment[CODESEG].imageUsed;
1386 	header.dataOffset = header.codeOffset + segment[CODESEG].imageUsed;
1387 	header.dataLength = segment[DATASEG].imageUsed;
1388 	header.litLength = segment[LITSEG].imageUsed;
1389 	header.bssLength = segment[BSSSEG].imageUsed;
1390 	header.jtrgLength = segment[JTRGSEG].imageUsed;
1391 
1392 	report( "Writing to %s\n", imageName );
1393 
1394 #ifdef Q3_BIG_ENDIAN
1395 	{
1396 		int i;
1397 
1398 		// byte swap the header
1399 		for ( i = 0 ; i < sizeof( vmHeader_t ) / 4 ; i++ ) {
1400 			((int *)&header)[i] = LittleLong( ((int *)&header)[i] );
1401 		}
1402 	}
1403 #endif
1404 
1405 	CreatePath( imageName );
1406 	f = SafeOpenWrite( imageName );
1407 	SafeWrite( f, &header, headerSize );
1408 	SafeWrite( f, &segment[CODESEG].image, segment[CODESEG].imageUsed );
1409 	SafeWrite( f, &segment[DATASEG].image, segment[DATASEG].imageUsed );
1410 	SafeWrite( f, &segment[LITSEG].image, segment[LITSEG].imageUsed );
1411 
1412 	if( !options.vanillaQ3Compatibility ) {
1413 		SafeWrite( f, &segment[JTRGSEG].image, segment[JTRGSEG].imageUsed );
1414 	}
1415 
1416 	fclose( f );
1417 }
1418 
1419 /*
1420 ===============
1421 Assemble
1422 ===============
1423 */
Assemble(void)1424 static void Assemble( void ) {
1425 	int		i;
1426 	char	filename[MAX_OS_PATH];
1427 	char		*ptr;
1428 
1429 	report( "outputFilename: %s\n", outputFilename );
1430 
1431 	for ( i = 0 ; i < numAsmFiles ; i++ ) {
1432 		strcpy( filename, asmFileNames[ i ] );
1433 		DefaultExtension( filename, ".asm" );
1434 		LoadFile( filename, (void **)&asmFiles[i] );
1435 	}
1436 
1437 	// assemble
1438 	for ( passNumber = 0 ; passNumber < 2 ; passNumber++ ) {
1439 		segment[LITSEG].segmentBase = segment[DATASEG].imageUsed;
1440 		segment[BSSSEG].segmentBase = segment[LITSEG].segmentBase + segment[LITSEG].imageUsed;
1441 		segment[JTRGSEG].segmentBase = segment[BSSSEG].segmentBase + segment[BSSSEG].imageUsed;
1442 		for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) {
1443 			segment[i].imageUsed = 0;
1444 		}
1445 		segment[DATASEG].imageUsed = 4;		// skip the 0 byte, so NULL pointers are fixed up properly
1446 		instructionCount = 0;
1447 
1448 		for ( i = 0 ; i < numAsmFiles ; i++ ) {
1449 			currentFileIndex = i;
1450 			currentFileName = asmFileNames[ i ];
1451 			currentFileLine = 0;
1452 			report("pass %i: %s\n", passNumber, currentFileName );
1453 			fflush( NULL );
1454 			ptr = asmFiles[i];
1455 			while ( ptr ) {
1456 				ptr = ExtractLine( ptr );
1457 				AssembleLine();
1458 			}
1459 		}
1460 
1461 		// align all segment
1462 		for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) {
1463 			segment[i].imageUsed = (segment[i].imageUsed + 3) & ~3;
1464 		}
1465 		if (passNumber == 0) {
1466 			sort_symbols();
1467 		}
1468 	}
1469 
1470 	// reserve the stack in bss
1471 	DefineSymbol( "_stackStart", segment[BSSSEG].imageUsed );
1472 	segment[BSSSEG].imageUsed += stackSize;
1473 	DefineSymbol( "_stackEnd", segment[BSSSEG].imageUsed );
1474 
1475 	// write the image
1476 	WriteVmFile();
1477 
1478 	// write the map file even if there were errors
1479 	if( options.writeMapFile ) {
1480 		WriteMapFile();
1481 	}
1482 }
1483 
1484 
1485 /*
1486 =============
1487 ParseOptionFile
1488 
1489 =============
1490 */
ParseOptionFile(const char * filename)1491 static void ParseOptionFile( const char *filename ) {
1492 	char		expanded[MAX_OS_PATH];
1493 	char		*text, *text_p;
1494 
1495 	strcpy( expanded, filename );
1496 	DefaultExtension( expanded, ".q3asm" );
1497 	LoadFile( expanded, (void **)&text );
1498 	if ( !text ) {
1499 		return;
1500 	}
1501 
1502 	text_p = text;
1503 
1504 	while( ( text_p = COM_Parse( text_p ) ) != 0 ) {
1505 		if ( !strcmp( com_token, "-o" ) ) {
1506 			// allow output override in option file
1507 			text_p = COM_Parse( text_p );
1508 			if ( text_p ) {
1509 				strcpy( outputFilename, com_token );
1510 			}
1511 			continue;
1512 		}
1513 
1514 		asmFileNames[ numAsmFiles ] = copystring( com_token );
1515 		numAsmFiles++;
1516 	}
1517 }
1518 
ShowHelp(char * argv0)1519 static void ShowHelp( char *argv0 ) {
1520 	Error("Usage: %s [OPTION]... [FILES]...\n\
1521 Assemble LCC bytecode assembly to Q3VM bytecode.\n\
1522 \n\
1523   -o OUTPUT      Write assembled output to file OUTPUT.qvm\n\
1524   -f LISTFILE    Read options and list of files to assemble from LISTFILE.q3asm\n\
1525   -b BUCKETS     Set symbol hash table to BUCKETS buckets\n\
1526   -m             Generate a mapfile for each OUTPUT.qvm\n\
1527   -v             Verbose compilation report\n\
1528   -vq3           Produce a qvm file compatible with Q3 1.32b\n\
1529   -h --help -?   Show this help\n\
1530 ", argv0);
1531 }
1532 
1533 /*
1534 ==============
1535 main
1536 ==============
1537 */
main(int argc,char ** argv)1538 int main( int argc, char **argv ) {
1539 	int			i;
1540 	double		start, end;
1541 
1542 //	_chdir( "/quake3/jccode/cgame/lccout" );	// hack for vc profiler
1543 
1544 	if ( argc < 2 ) {
1545 		ShowHelp( argv[0] );
1546 	}
1547 
1548 	start = I_FloatTime ();
1549 
1550 	// default filename is "q3asm"
1551 	strcpy( outputFilename, "q3asm" );
1552 	numAsmFiles = 0;
1553 
1554 	for ( i = 1 ; i < argc ; i++ ) {
1555 		if ( argv[i][0] != '-' ) {
1556 			break;
1557 		}
1558 		if( !strcmp( argv[ i ], "-h" ) ||
1559 		    !strcmp( argv[ i ], "--help" ) ||
1560 		    !strcmp( argv[ i ], "-?") ) {
1561 			ShowHelp( argv[0] );
1562 		}
1563 
1564 		if ( !strcmp( argv[i], "-o" ) ) {
1565 			if ( i == argc - 1 ) {
1566 				Error( "-o must preceed a filename" );
1567 			}
1568 /* Timbo of Tremulous pointed out -o not working; stock ID q3asm folded in the change. Yay. */
1569 			strcpy( outputFilename, argv[ i+1 ] );
1570 			i++;
1571 			continue;
1572 		}
1573 
1574 		if ( !strcmp( argv[i], "-f" ) ) {
1575 			if ( i == argc - 1 ) {
1576 				Error( "-f must preceed a filename" );
1577 			}
1578 			ParseOptionFile( argv[ i+1 ] );
1579 			i++;
1580 			continue;
1581 		}
1582 
1583 		if (!strcmp(argv[i], "-b")) {
1584 			if (i == argc - 1) {
1585 				Error("-b requires an argument");
1586 			}
1587 			i++;
1588 			symtablelen = atoiNoCap(argv[i]);
1589 			continue;
1590 		}
1591 
1592 		if( !strcmp( argv[ i ], "-v" ) ) {
1593 /* Verbosity option added by Timbo, 2002.09.14.
1594 By default (no -v option), q3asm remains silent except for critical errors.
1595 Verbosity turns on all messages, error or not.
1596 Motivation: not wanting to scrollback for pages to find asm error.
1597 */
1598 			options.verbose = qtrue;
1599 			continue;
1600 		}
1601 
1602 		if( !strcmp( argv[ i ], "-m" ) ) {
1603 			options.writeMapFile = qtrue;
1604 			continue;
1605 		}
1606 
1607 		if( !strcmp( argv[ i ], "-vq3" ) ) {
1608 			options.vanillaQ3Compatibility = qtrue;
1609 			continue;
1610 		}
1611 
1612 		Error( "Unknown option: %s", argv[i] );
1613 	}
1614 
1615 	// the rest of the command line args are asm files
1616 	for ( ; i < argc ; i++ ) {
1617 		asmFileNames[ numAsmFiles ] = copystring( argv[ i ] );
1618 		numAsmFiles++;
1619 	}
1620 	// In some case it Segfault without this check
1621 	if ( numAsmFiles == 0 ) {
1622 		Error( "No file to assemble\n" );
1623 	}
1624 
1625 	InitTables();
1626 	Assemble();
1627 
1628 	{
1629 		symbol_t *s;
1630 
1631 		for ( i = 0, s = symbols ; s ; s = s->next, i++ ) /* nop */ ;
1632 
1633 		if (options.verbose)
1634 		{
1635 			report("%d symbols defined\n", i);
1636 			hashtable_stats(symtable);
1637 			hashtable_stats(optable);
1638 		}
1639 	}
1640 
1641 	end = I_FloatTime ();
1642 	report ("%5.0f seconds elapsed\n", end-start);
1643 
1644 	return errorCount;
1645 }
1646 
1647