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