diff options
Diffstat (limited to 'ioq3-r437/src/tools/asm/q3asm.c')
-rw-r--r-- | ioq3-r437/src/tools/asm/q3asm.c | 1628 |
1 files changed, 0 insertions, 1628 deletions
diff --git a/ioq3-r437/src/tools/asm/q3asm.c b/ioq3-r437/src/tools/asm/q3asm.c deleted file mode 100644 index 93af582f..00000000 --- a/ioq3-r437/src/tools/asm/q3asm.c +++ /dev/null @@ -1,1628 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "cmdlib.h" -#include "mathlib.h" -#include "../../qcommon/qfiles.h" - -/* 19079 total symbols in FI, 2002 Jan 23 */ -#define DEFAULT_HASHTABLE_SIZE 2048 - -char outputFilename[MAX_OS_PATH]; - -// the zero page size is just used for detecting run time faults -#define ZERO_PAGE_SIZE 0 // 256 - -typedef enum { - OP_UNDEF, - - OP_IGNORE, - - OP_BREAK, - - OP_ENTER, - OP_LEAVE, - OP_CALL, - OP_PUSH, - OP_POP, - - OP_CONST, - OP_LOCAL, - - OP_JUMP, - - //------------------- - - OP_EQ, - OP_NE, - - OP_LTI, - OP_LEI, - OP_GTI, - OP_GEI, - - OP_LTU, - OP_LEU, - OP_GTU, - OP_GEU, - - OP_EQF, - OP_NEF, - - OP_LTF, - OP_LEF, - OP_GTF, - OP_GEF, - - //------------------- - - OP_LOAD1, - OP_LOAD2, - OP_LOAD4, - OP_STORE1, - OP_STORE2, - OP_STORE4, // *(stack[top-1]) = stack[yop - OP_ARG, - OP_BLOCK_COPY, - - //------------------- - - OP_SEX8, - OP_SEX16, - - OP_NEGI, - OP_ADD, - OP_SUB, - OP_DIVI, - OP_DIVU, - OP_MODI, - OP_MODU, - OP_MULI, - OP_MULU, - - OP_BAND, - OP_BOR, - OP_BXOR, - OP_BCOM, - - OP_LSH, - OP_RSHI, - OP_RSHU, - - OP_NEGF, - OP_ADDF, - OP_SUBF, - OP_DIVF, - OP_MULF, - - OP_CVIF, - OP_CVFI -} opcode_t; - -typedef struct { - int imageBytes; // after decompression - int entryPoint; - int stackBase; - int stackSize; -} executableHeader_t; - -typedef enum { - CODESEG, - DATASEG, // initialized 32 bit data, will be byte swapped - LITSEG, // strings - BSSSEG, // 0 filled - JTRGSEG, // psuedo-segment that contains only jump table targets - NUM_SEGMENTS -} segmentName_t; - -#define MAX_IMAGE 0x400000 - -typedef struct { - byte image[MAX_IMAGE]; - int imageUsed; - int segmentBase; // only valid on second pass -} segment_t; - -typedef struct symbol_s { - struct symbol_s *next; - int hash; - segment_t *segment; - char *name; - int value; -} symbol_t; - -typedef struct hashchain_s { - void *data; - struct hashchain_s *next; -} hashchain_t; - -typedef struct hashtable_s { - int buckets; - hashchain_t **table; -} hashtable_t; - -int symtablelen = DEFAULT_HASHTABLE_SIZE; -hashtable_t *symtable; -hashtable_t *optable; - -segment_t segment[NUM_SEGMENTS]; -segment_t *currentSegment; - -int passNumber; - -int numSymbols; -int errorCount; - -typedef struct options_s { - qboolean verbose; - qboolean writeMapFile; - qboolean vanillaQ3Compatibility; -} options_t; - -options_t options = { 0 }; - -symbol_t *symbols; -symbol_t *lastSymbol = 0; /* Most recent symbol defined. */ - - -#define MAX_ASM_FILES 256 -int numAsmFiles; -char *asmFiles[MAX_ASM_FILES]; -char *asmFileNames[MAX_ASM_FILES]; - -int currentFileIndex; -char *currentFileName; -int currentFileLine; - -//int stackSize = 16384; -int stackSize = 0x10000; - -// we need to convert arg and ret instructions to -// stores to the local stack frame, so we need to track the -// characteristics of the current functions stack frame -int currentLocals; // bytes of locals needed by this function -int currentArgs; // bytes of largest argument list called from this function -int currentArgOffset; // byte offset in currentArgs to store next arg, reset each call - -#define MAX_LINE_LENGTH 1024 -char lineBuffer[MAX_LINE_LENGTH]; -int lineParseOffset; -char token[MAX_LINE_LENGTH]; - -int instructionCount; - -typedef struct { - char *name; - int opcode; -} sourceOps_t; - -sourceOps_t sourceOps[] = { -#include "opstrings.h" -}; - -#define NUM_SOURCE_OPS ( sizeof( sourceOps ) / sizeof( sourceOps[0] ) ) - -int opcodesHash[ NUM_SOURCE_OPS ]; - - - -int -vreport (const char* fmt, va_list vp) -{ - if (options.verbose != qtrue) - return 0; - return vprintf(fmt, vp); -} - -int -report (const char *fmt, ...) -{ - va_list va; - int retval; - - va_start(va, fmt); - retval = vreport(fmt, va); - va_end(va); - return retval; -} - -/* The chain-and-bucket hash table. -PH */ - -void -hashtable_init (hashtable_t *H, int buckets) -{ - H->buckets = buckets; - H->table = calloc(H->buckets, sizeof(*(H->table))); - return; -} - -hashtable_t * -hashtable_new (int buckets) -{ - hashtable_t *H; - - H = malloc(sizeof(hashtable_t)); - hashtable_init(H, buckets); - return H; -} - -/* No destroy/destructor. No need. */ - -void -hashtable_add (hashtable_t *H, int hashvalue, void *datum) -{ - hashchain_t *hc, **hb; - - hashvalue = (abs(hashvalue) % H->buckets); - hb = &(H->table[hashvalue]); - if (*hb == 0) - { - /* Empty bucket. Create new one. */ - *hb = calloc(1, sizeof(**hb)); - hc = *hb; - } - else - { - /* Get hc to point to last node in chain. */ - for (hc = *hb; hc && hc->next; hc = hc->next); - hc->next = calloc(1, sizeof(*hc)); - hc = hc->next; - } - hc->data = datum; - hc->next = 0; - return; -} - -hashchain_t * -hashtable_get (hashtable_t *H, int hashvalue) -{ - hashvalue = (abs(hashvalue) % H->buckets); - return (H->table[hashvalue]); -} - -void -hashtable_stats (hashtable_t *H) -{ - int len, empties, longest, nodes; - int i; - float meanlen; - hashchain_t *hc; - - report("Stats for hashtable %08X", H); - empties = 0; - longest = 0; - nodes = 0; - for (i = 0; i < H->buckets; i++) - { - if (H->table[i] == 0) - { empties++; continue; } - for (hc = H->table[i], len = 0; hc; hc = hc->next, len++); - if (len > longest) { longest = len; } - nodes += len; - } - meanlen = (float)(nodes) / (H->buckets - empties); -#if 0 -/* Long stats display */ - report(" Total buckets: %d\n", H->buckets); - report(" Total stored nodes: %d\n", nodes); - report(" Longest chain: %d\n", longest); - report(" Empty chains: %d\n", empties); - report(" Mean non-empty chain length: %f\n", meanlen); -#else //0 -/* Short stats display */ - report(", %d buckets, %d nodes", H->buckets, nodes); - report("\n"); - report(" Longest chain: %d, empty chains: %d, mean non-empty: %f", longest, empties, meanlen); -#endif //0 - report("\n"); -} - - -/* Kludge. */ -/* Check if symbol already exists. */ -/* Returns 0 if symbol does NOT already exist, non-zero otherwise. */ -int -hashtable_symbol_exists (hashtable_t *H, int hash, char *sym) -{ - hashchain_t *hc; - symbol_t *s; - - hash = (abs(hash) % H->buckets); - hc = H->table[hash]; - if (hc == 0) - { - /* Empty chain means this symbol has not yet been defined. */ - return 0; - } - for (; hc; hc = hc->next) - { - s = (symbol_t*)hc->data; -// if ((hash == s->hash) && (strcmp(sym, s->name) == 0)) -/* We _already_ know the hash is the same. That's why we're probing! */ - if (strcmp(sym, s->name) == 0) - { - /* Symbol collisions -- symbol already exists. */ - return 1; - } - } - return 0; /* Can't find collision. */ -} - - - - -/* Comparator function for quicksorting. */ -int -symlist_cmp (const void *e1, const void *e2) -{ - const symbol_t *a, *b; - - a = *(const symbol_t **)e1; - b = *(const symbol_t **)e2; -//crumb("Symbol comparison (1) %d to (2) %d\n", a->value, b->value); - return ( a->value - b->value); -} - -/* - Sort the symbols list by using QuickSort (qsort()). - This may take a LOT of memory (a few megabytes?), but memory is cheap these days. - However, qsort(3) already exists, and I'm really lazy. - -PH -*/ -void -sort_symbols () -{ - int i, elems; - symbol_t *s; - symbol_t **symlist; - -//crumb("sort_symbols: Constructing symlist array\n"); - for (elems = 0, s = symbols; s; s = s->next, elems++) /* nop */ ; - symlist = malloc(elems * sizeof(symbol_t*)); - for (i = 0, s = symbols; s; s = s->next, i++) - { - symlist[i] = s; - } -//crumbf("sort_symbols: Quick-sorting %d symbols\n", elems); - qsort(symlist, elems, sizeof(symbol_t*), symlist_cmp); -//crumbf("sort_symbols: Reconstructing symbols list\n"); - s = symbols = symlist[0]; - for (i = 1; i < elems; i++) - { - s->next = symlist[i]; - s = s->next; - } - lastSymbol = s; - s->next = 0; -//crumbf("sort_symbols: verifying..."); fflush(stdout); - for (i = 0, s = symbols; s; s = s->next, i++) /*nop*/ ; -//crumbf(" %d elements\n", i); - free(symlist); /* d'oh. no gc. */ -} - - -#ifdef _MSC_VER -#define INT64 __int64 -#define atoi64 _atoi64 -#else -#define INT64 long long int -#define atoi64 atoll -#endif - -/* - Problem: - BYTE values are specified as signed decimal string. A properly functional - atoip() will cap large signed values at 0x7FFFFFFF. Negative word values are - often specified as very large decimal values by lcc. Therefore, values that - should be between 0x7FFFFFFF and 0xFFFFFFFF come out as 0x7FFFFFFF when using - atoi(). Bad. - - This function is one big evil hack to work around this problem. -*/ -int atoiNoCap (const char *s) -{ - INT64 l; - union { - unsigned int u; - signed int i; - } retval; - - l = atoi64(s); - /* Now smash to signed 32 bits accordingly. */ - if (l < 0) { - retval.i = (int)l; - } else { - retval.u = (unsigned int)l; - } - return retval.i; /* <- union hackage. I feel dirty with this. -PH */ -} - - - -/* -============= -HashString -============= -*/ -/* Default hash function of Kazlib 1.19, slightly modified. */ -unsigned int HashString (const char *key) -{ - static unsigned long randbox[] = { - 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, - 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, - 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, - 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, - }; - - const char *str = key; - unsigned int acc = 0; - - while (*str) { - acc ^= randbox[(*str + acc) & 0xf]; - acc = (acc << 1) | (acc >> 31); - acc &= 0xffffffffU; - acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; - acc = (acc << 2) | (acc >> 30); - acc &= 0xffffffffU; - } - return abs(acc); -} - - -/* -============ -CodeError -============ -*/ -void CodeError( char *fmt, ... ) { - va_list argptr; - - errorCount++; - - report( "%s:%i ", currentFileName, currentFileLine ); - - va_start( argptr,fmt ); - vprintf( fmt,argptr ); - va_end( argptr ); -} - -/* -============ -EmitByte -============ -*/ -void EmitByte( segment_t *seg, int v ) { - if ( seg->imageUsed >= MAX_IMAGE ) { - Error( "MAX_IMAGE" ); - } - seg->image[ seg->imageUsed ] = v; - seg->imageUsed++; -} - -/* -============ -EmitInt -============ -*/ -void EmitInt( segment_t *seg, int v ) { - if ( seg->imageUsed >= MAX_IMAGE - 4) { - Error( "MAX_IMAGE" ); - } - seg->image[ seg->imageUsed ] = v & 255; - seg->image[ seg->imageUsed + 1 ] = ( v >> 8 ) & 255; - seg->image[ seg->imageUsed + 2 ] = ( v >> 16 ) & 255; - seg->image[ seg->imageUsed + 3 ] = ( v >> 24 ) & 255; - seg->imageUsed += 4; -} - -/* -============ -DefineSymbol - -Symbols can only be defined on pass 0 -============ -*/ -void DefineSymbol( char *sym, int value ) { - /* Hand optimization by PhaethonH */ - symbol_t *s; - char expanded[MAX_LINE_LENGTH]; - int hash; - - if ( passNumber == 1 ) { - return; - } - - // add the file prefix to local symbols to guarantee unique - if ( sym[0] == '$' ) { - sprintf( expanded, "%s_%i", sym, currentFileIndex ); - sym = expanded; - } - - hash = HashString( sym ); - - if (hashtable_symbol_exists(symtable, hash, sym)) { - CodeError( "Multiple definitions for %s\n", sym ); - return; - } - - s = malloc( sizeof( *s ) ); - s->next = NULL; - s->name = copystring( sym ); - s->hash = hash; - s->value = value; - s->segment = currentSegment; - - hashtable_add(symtable, hash, s); - -/* - Hash table lookup already speeds up symbol lookup enormously. - We postpone sorting until end of pass 0. - Since we're not doing the insertion sort, lastSymbol should always - wind up pointing to the end of list. - This allows constant time for adding to the list. - -PH -*/ - if (symbols == 0) { - lastSymbol = symbols = s; - } else { - lastSymbol->next = s; - lastSymbol = s; - } -} - - -/* -============ -LookupSymbol - -Symbols can only be evaluated on pass 1 -============ -*/ -int LookupSymbol( char *sym ) { - symbol_t *s; - char expanded[MAX_LINE_LENGTH]; - int hash; - hashchain_t *hc; - - if ( passNumber == 0 ) { - return 0; - } - - // add the file prefix to local symbols to guarantee unique - if ( sym[0] == '$' ) { - sprintf( expanded, "%s_%i", sym, currentFileIndex ); - sym = expanded; - } - - hash = HashString( sym ); - -/* - Hand optimization by PhaethonH - - Using a hash table with chain/bucket for lookups alone sped up q3asm by almost 3x for me. - -PH -*/ - for (hc = hashtable_get(symtable, hash); hc; hc = hc->next) { - s = (symbol_t*)hc->data; /* ugly typecasting, but it's fast! */ - if ( (hash == s->hash) && !strcmp(sym, s->name) ) { - return s->segment->segmentBase + s->value; - } - } - - CodeError( "error: symbol %s undefined\n", sym ); - passNumber = 0; - DefineSymbol( sym, 0 ); // so more errors aren't printed - passNumber = 1; - return 0; -} - - -/* -============== -ExtractLine - -Extracts the next line from the given text block. -If a full line isn't parsed, returns NULL -Otherwise returns the updated parse pointer -=============== -*/ -char *ExtractLine( char *data ) { -/* Goal: - Given a string `data', extract one text line into buffer `lineBuffer' that - is no longer than MAX_LINE_LENGTH characters long. Return value is - remainder of `data' that isn't part of `lineBuffer'. - -PH -*/ - /* Hand-optimized by PhaethonH */ - char *p, *q; - - currentFileLine++; - - lineParseOffset = 0; - token[0] = 0; - *lineBuffer = 0; - - p = q = data; - if (!*q) { - return NULL; - } - - for ( ; !((*p == 0) || (*p == '\n')); p++) /* nop */ ; - - if ((p - q) >= MAX_LINE_LENGTH) { - CodeError( "MAX_LINE_LENGTH" ); - return data; - } - - memcpy( lineBuffer, data, (p - data) ); - lineBuffer[(p - data)] = 0; - p += (*p == '\n') ? 1 : 0; /* Skip over final newline. */ - return p; -} - - -/* -============== -Parse - -Parse a token out of linebuffer -============== -*/ -qboolean Parse( void ) { - /* Hand-optimized by PhaethonH */ - const char *p, *q; - - /* Because lineParseOffset is only updated just before exit, this makes this code version somewhat harder to debug under a symbolic debugger. */ - - *token = 0; /* Clear token. */ - - // skip whitespace - for (p = lineBuffer + lineParseOffset; *p && (*p <= ' '); p++) /* nop */ ; - - // skip ; comments - /* die on end-of-string */ - if ((*p == ';') || (*p == 0)) { - lineParseOffset = p - lineBuffer; - return qfalse; - } - - q = p; /* Mark the start of token. */ - /* Find separator first. */ - for ( ; *p > 32; p++) /* nop */ ; /* XXX: unsafe assumptions. */ - /* *p now sits on separator. Mangle other values accordingly. */ - strncpy(token, q, p - q); - token[p - q] = 0; - - lineParseOffset = p - lineBuffer; - - return qtrue; -} - - -/* -============== -ParseValue -============== -*/ -int ParseValue( void ) { - Parse(); - return atoiNoCap( token ); -} - - -/* -============== -ParseExpression -============== -*/ -int ParseExpression(void) { - /* Hand optimization, PhaethonH */ - int i, j; - char sym[MAX_LINE_LENGTH]; - int v; - - /* Skip over a leading minus. */ - for ( i = ((token[0] == '-') ? 1 : 0) ; i < MAX_LINE_LENGTH ; i++ ) { - if ( token[i] == '+' || token[i] == '-' || token[i] == 0 ) { - break; - } - } - - memcpy( sym, token, i ); - sym[i] = 0; - - switch (*sym) { /* Resolve depending on first character. */ -/* Optimizing compilers can convert cases into "calculated jumps". I think these are faster. -PH */ - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - v = atoiNoCap(sym); - break; - default: - v = LookupSymbol(sym); - break; - } - - // parse add / subtract offsets - while ( token[i] != 0 ) { - for ( j = i + 1 ; j < MAX_LINE_LENGTH ; j++ ) { - if ( token[j] == '+' || token[j] == '-' || token[j] == 0 ) { - break; - } - } - - memcpy( sym, token+i+1, j-i-1 ); - sym[j-i-1] = 0; - - switch (token[i]) { - case '+': - v += atoiNoCap(sym); - break; - case '-': - v -= atoiNoCap(sym); - break; - } - - i = j; - } - - return v; -} - - -/* -============== -HackToSegment - -BIG HACK: I want to put all 32 bit values in the data -segment so they can be byte swapped, and all char data in the lit -segment, but switch jump tables are emited in the lit segment and -initialized strng variables are put in the data segment. - -I can change segments here, but I also need to fixup the -label that was just defined - -Note that the lit segment is read-write in the VM, so strings -aren't read only as in some architectures. -============== -*/ -void HackToSegment( segmentName_t seg ) { - if ( currentSegment == &segment[seg] ) { - return; - } - - currentSegment = &segment[seg]; - if ( passNumber == 0 ) { - lastSymbol->segment = currentSegment; - lastSymbol->value = currentSegment->imageUsed; - } -} - - - - - - - -//#define STAT(L) report("STAT " L "\n"); -#define STAT(L) -#define ASM(O) int TryAssemble##O () - - -/* - These clauses were moved out from AssembleLine() to allow reordering of if's. - An optimizing compiler should reconstruct these back into inline code. - -PH -*/ - - // call instructions reset currentArgOffset -ASM(CALL) -{ - if ( !strncmp( token, "CALL", 4 ) ) { -STAT("CALL"); - EmitByte( &segment[CODESEG], OP_CALL ); - instructionCount++; - currentArgOffset = 0; - return 1; - } - return 0; -} - - // arg is converted to a reversed store -ASM(ARG) -{ - if ( !strncmp( token, "ARG", 3 ) ) { -STAT("ARG"); - EmitByte( &segment[CODESEG], OP_ARG ); - instructionCount++; - if ( 8 + currentArgOffset >= 256 ) { - CodeError( "currentArgOffset >= 256" ); - return 1; - } - EmitByte( &segment[CODESEG], 8 + currentArgOffset ); - currentArgOffset += 4; - return 1; - } - return 0; -} - - // ret just leaves something on the op stack -ASM(RET) -{ - if ( !strncmp( token, "RET", 3 ) ) { -STAT("RET"); - EmitByte( &segment[CODESEG], OP_LEAVE ); - instructionCount++; - EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); - return 1; - } - return 0; -} - - // pop is needed to discard the return value of - // a function -ASM(POP) -{ - if ( !strncmp( token, "pop", 3 ) ) { -STAT("POP"); - EmitByte( &segment[CODESEG], OP_POP ); - instructionCount++; - return 1; - } - return 0; -} - - // address of a parameter is converted to OP_LOCAL -ASM(ADDRF) -{ - int v; - if ( !strncmp( token, "ADDRF", 5 ) ) { -STAT("ADDRF"); - instructionCount++; - Parse(); - v = ParseExpression(); - v = 16 + currentArgs + currentLocals + v; - EmitByte( &segment[CODESEG], OP_LOCAL ); - EmitInt( &segment[CODESEG], v ); - return 1; - } - return 0; -} - - // address of a local is converted to OP_LOCAL -ASM(ADDRL) -{ - int v; - if ( !strncmp( token, "ADDRL", 5 ) ) { -STAT("ADDRL"); - instructionCount++; - Parse(); - v = ParseExpression(); - v = 8 + currentArgs + v; - EmitByte( &segment[CODESEG], OP_LOCAL ); - EmitInt( &segment[CODESEG], v ); - return 1; - } - return 0; -} - -ASM(PROC) -{ - char name[1024]; - if ( !strcmp( token, "proc" ) ) { -STAT("PROC"); - Parse(); // function name - strcpy( name, token ); - - DefineSymbol( token, instructionCount ); // segment[CODESEG].imageUsed ); - - currentLocals = ParseValue(); // locals - currentLocals = ( currentLocals + 3 ) & ~3; - currentArgs = ParseValue(); // arg marshalling - currentArgs = ( currentArgs + 3 ) & ~3; - - if ( 8 + currentLocals + currentArgs >= 32767 ) { - CodeError( "Locals > 32k in %s\n", name ); - } - - instructionCount++; - EmitByte( &segment[CODESEG], OP_ENTER ); - EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); - return 1; - } - return 0; -} - - -ASM(ENDPROC) -{ - int v, v2; - if ( !strcmp( token, "endproc" ) ) { -STAT("ENDPROC"); - Parse(); // skip the function name - v = ParseValue(); // locals - v2 = ParseValue(); // arg marshalling - - // all functions must leave something on the opstack - instructionCount++; - EmitByte( &segment[CODESEG], OP_PUSH ); - - instructionCount++; - EmitByte( &segment[CODESEG], OP_LEAVE ); - EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); - - return 1; - } - return 0; -} - - -ASM(ADDRESS) -{ - int v; - if ( !strcmp( token, "address" ) ) { -STAT("ADDRESS"); - Parse(); - v = ParseExpression(); - -/* Addresses are 32 bits wide, and therefore go into data segment. */ - HackToSegment( DATASEG ); - EmitInt( currentSegment, v ); - if( passNumber == 1 && token[ 0 ] == '$' ) // crude test for labels - EmitInt( &segment[ JTRGSEG ], v ); - return 1; - } - return 0; -} - -ASM(EXPORT) -{ - if ( !strcmp( token, "export" ) ) { -STAT("EXPORT"); - return 1; - } - return 0; -} - -ASM(IMPORT) -{ - if ( !strcmp( token, "import" ) ) { -STAT("IMPORT"); - return 1; - } - return 0; -} - -ASM(CODE) -{ - if ( !strcmp( token, "code" ) ) { -STAT("CODE"); - currentSegment = &segment[CODESEG]; - return 1; - } - return 0; -} - -ASM(BSS) -{ - if ( !strcmp( token, "bss" ) ) { -STAT("BSS"); - currentSegment = &segment[BSSSEG]; - return 1; - } - return 0; -} - -ASM(DATA) -{ - if ( !strcmp( token, "data" ) ) { -STAT("DATA"); - currentSegment = &segment[DATASEG]; - return 1; - } - return 0; -} - -ASM(LIT) -{ - if ( !strcmp( token, "lit" ) ) { -STAT("LIT"); - currentSegment = &segment[LITSEG]; - return 1; - } - return 0; -} - -ASM(LINE) -{ - if ( !strcmp( token, "line" ) ) { -STAT("LINE"); - return 1; - } - return 0; -} - -ASM(FILE) -{ - if ( !strcmp( token, "file" ) ) { -STAT("FILE"); - return 1; - } - return 0; -} - -ASM(EQU) -{ - char name[1024]; - if ( !strcmp( token, "equ" ) ) { -STAT("EQU"); - Parse(); - strcpy( name, token ); - Parse(); - DefineSymbol( name, atoiNoCap(token) ); - return 1; - } - return 0; -} - -ASM(ALIGN) -{ - int v; - if ( !strcmp( token, "align" ) ) { -STAT("ALIGN"); - v = ParseValue(); - currentSegment->imageUsed = (currentSegment->imageUsed + v - 1 ) & ~( v - 1 ); - return 1; - } - return 0; -} - -ASM(SKIP) -{ - int v; - if ( !strcmp( token, "skip" ) ) { -STAT("SKIP"); - v = ParseValue(); - currentSegment->imageUsed += v; - return 1; - } - return 0; -} - -ASM(BYTE) -{ - int i, v, v2; - if ( !strcmp( token, "byte" ) ) { -STAT("BYTE"); - v = ParseValue(); - v2 = ParseValue(); - - if ( v == 1 ) { -/* Character (1-byte) values go into lit(eral) segment. */ - HackToSegment( LITSEG ); - } else if ( v == 4 ) { -/* 32-bit (4-byte) values go into data segment. */ - HackToSegment( DATASEG ); - } else if ( v == 2 ) { -/* and 16-bit (2-byte) values will cause q3asm to barf. */ - CodeError( "16 bit initialized data not supported" ); - } - - // emit little endien - for ( i = 0 ; i < v ; i++ ) { - EmitByte( currentSegment, (v2 & 0xFF) ); /* paranoid ANDing -PH */ - v2 >>= 8; - } - return 1; - } - return 0; -} - - // code labels are emited as instruction counts, not byte offsets, - // because the physical size of the code will change with - // different run time compilers and we want to minimize the - // size of the required translation table -ASM(LABEL) -{ - if ( !strncmp( token, "LABEL", 5 ) ) { -STAT("LABEL"); - Parse(); - if ( currentSegment == &segment[CODESEG] ) { - DefineSymbol( token, instructionCount ); - } else { - DefineSymbol( token, currentSegment->imageUsed ); - } - return 1; - } - return 0; -} - - - -/* -============== -AssembleLine - -============== -*/ -void AssembleLine( void ) { - hashchain_t *hc; - sourceOps_t *op; - int i; - int hash; - - Parse(); - if ( !token[0] ) { - return; - } - - hash = HashString( token ); - -/* - Opcode search using hash table. - Since the opcodes stays mostly fixed, this may benefit even more from a tree. - Always with the tree :) - -PH -*/ - for (hc = hashtable_get(optable, hash); hc; hc = hc->next) { - op = (sourceOps_t*)(hc->data); - i = op - sourceOps; - if ((hash == opcodesHash[i]) && (!strcmp(token, op->name))) { - int opcode; - int expression; - - if ( op->opcode == OP_UNDEF ) { - CodeError( "Undefined opcode: %s\n", token ); - } - if ( op->opcode == OP_IGNORE ) { - return; // we ignore most conversions - } - - // sign extensions need to check next parm - opcode = op->opcode; - if ( opcode == OP_SEX8 ) { - Parse(); - if ( token[0] == '1' ) { - opcode = OP_SEX8; - } else if ( token[0] == '2' ) { - opcode = OP_SEX16; - } else { - CodeError( "Bad sign extension: %s\n", token ); - return; - } - } - - // check for expression - Parse(); - if ( token[0] && op->opcode != OP_CVIF - && op->opcode != OP_CVFI ) { - expression = ParseExpression(); - - // code like this can generate non-dword block copies: - // auto char buf[2] = " "; - // we are just going to round up. This might conceivably - // be incorrect if other initialized chars follow. - if ( opcode == OP_BLOCK_COPY ) { - expression = ( expression + 3 ) & ~3; - } - - EmitByte( &segment[CODESEG], opcode ); - EmitInt( &segment[CODESEG], expression ); - } else { - EmitByte( &segment[CODESEG], opcode ); - } - - instructionCount++; - return; - } - } - -/* This falls through if an assembly opcode is not found. -PH */ - -/* The following should be sorted in sequence of statistical frequency, most frequent first. -PH */ -/* -Empirical frequency statistics from FI 2001.01.23: - 109892 STAT ADDRL - 72188 STAT BYTE - 51150 STAT LINE - 50906 STAT ARG - 43704 STAT IMPORT - 34902 STAT LABEL - 32066 STAT ADDRF - 23704 STAT CALL - 7720 STAT POP - 7256 STAT RET - 5198 STAT ALIGN - 3292 STAT EXPORT - 2878 STAT PROC - 2878 STAT ENDPROC - 2812 STAT ADDRESS - 738 STAT SKIP - 374 STAT EQU - 280 STAT CODE - 176 STAT LIT - 102 STAT FILE - 100 STAT BSS - 68 STAT DATA - - -PH -*/ - -#undef ASM -#define ASM(O) if (TryAssemble##O ()) return; - - ASM(ADDRL) - ASM(BYTE) - ASM(LINE) - ASM(ARG) - ASM(IMPORT) - ASM(LABEL) - ASM(ADDRF) - ASM(CALL) - ASM(POP) - ASM(RET) - ASM(ALIGN) - ASM(EXPORT) - ASM(PROC) - ASM(ENDPROC) - ASM(ADDRESS) - ASM(SKIP) - ASM(EQU) - ASM(CODE) - ASM(LIT) - ASM(FILE) - ASM(BSS) - ASM(DATA) - - CodeError( "Unknown token: %s\n", token ); -} - -/* -============== -InitTables -============== -*/ -void InitTables( void ) { - int i; - - symtable = hashtable_new(symtablelen); - optable = hashtable_new(100); /* There's hardly 100 opcodes anyway. */ - - for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) { - opcodesHash[i] = HashString( sourceOps[i].name ); - hashtable_add(optable, opcodesHash[i], sourceOps + i); - } -} - - -/* -============== -WriteMapFile -============== -*/ -void WriteMapFile( void ) { - FILE *f; - symbol_t *s; - char imageName[MAX_OS_PATH]; - int seg; - - strcpy( imageName, outputFilename ); - StripExtension( imageName ); - strcat( imageName, ".map" ); - - report( "Writing %s...\n", imageName ); - - f = SafeOpenWrite( imageName ); - for ( seg = CODESEG ; seg <= BSSSEG ; seg++ ) { - for ( s = symbols ; s ; s = s->next ) { - if ( s->name[0] == '$' ) { - continue; // skip locals - } - if ( &segment[seg] != s->segment ) { - continue; - } - fprintf( f, "%i %8x %s\n", seg, s->value, s->name ); - } - } - fclose( f ); -} - -/* -=============== -WriteVmFile -=============== -*/ -void WriteVmFile( void ) { - char imageName[MAX_OS_PATH]; - vmHeader_t header; - FILE *f; - int headerSize; - - report( "%i total errors\n", errorCount ); - - strcpy( imageName, outputFilename ); - StripExtension( imageName ); - strcat( imageName, ".qvm" ); - - remove( imageName ); - - report( "code segment: %7i\n", segment[CODESEG].imageUsed ); - report( "data segment: %7i\n", segment[DATASEG].imageUsed ); - report( "lit segment: %7i\n", segment[LITSEG].imageUsed ); - report( "bss segment: %7i\n", segment[BSSSEG].imageUsed ); - report( "instruction count: %i\n", instructionCount ); - - if ( errorCount != 0 ) { - report( "Not writing a file due to errors\n" ); - return; - } - - if( !options.vanillaQ3Compatibility ) { - header.vmMagic = VM_MAGIC_VER2; - headerSize = sizeof( header ); - } else { - header.vmMagic = VM_MAGIC; - - // Don't write the VM_MAGIC_VER2 bits when maintaining 1.32b compatibility. - // (I know this isn't strictly correct due to padding, but then platforms - // that pad wouldn't be able to write a correct header anyway). Note: if - // vmHeader_t changes, this needs to be adjusted too. - headerSize = sizeof( header ) - sizeof( header.jtrgLength ); - } - - header.instructionCount = instructionCount; - header.codeOffset = headerSize; - header.codeLength = segment[CODESEG].imageUsed; - header.dataOffset = header.codeOffset + segment[CODESEG].imageUsed; - header.dataLength = segment[DATASEG].imageUsed; - header.litLength = segment[LITSEG].imageUsed; - header.bssLength = segment[BSSSEG].imageUsed; - header.jtrgLength = segment[JTRGSEG].imageUsed; - - report( "Writing to %s\n", imageName ); - - CreatePath( imageName ); - f = SafeOpenWrite( imageName ); - SafeWrite( f, &header, headerSize ); - SafeWrite( f, &segment[CODESEG].image, segment[CODESEG].imageUsed ); - SafeWrite( f, &segment[DATASEG].image, segment[DATASEG].imageUsed ); - SafeWrite( f, &segment[LITSEG].image, segment[LITSEG].imageUsed ); - - if( !options.vanillaQ3Compatibility ) { - SafeWrite( f, &segment[JTRGSEG].image, segment[JTRGSEG].imageUsed ); - } - - fclose( f ); -} - -/* -=============== -Assemble -=============== -*/ -void Assemble( void ) { - int i; - char filename[MAX_OS_PATH]; - char *ptr; - - report( "outputFilename: %s\n", outputFilename ); - - for ( i = 0 ; i < numAsmFiles ; i++ ) { - strcpy( filename, asmFileNames[ i ] ); - DefaultExtension( filename, ".asm" ); - LoadFile( filename, (void **)&asmFiles[i] ); - } - - // assemble - for ( passNumber = 0 ; passNumber < 2 ; passNumber++ ) { - segment[LITSEG].segmentBase = segment[DATASEG].imageUsed; - segment[BSSSEG].segmentBase = segment[LITSEG].segmentBase + segment[LITSEG].imageUsed; - segment[JTRGSEG].segmentBase = segment[BSSSEG].segmentBase + segment[BSSSEG].imageUsed; - for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) { - segment[i].imageUsed = 0; - } - segment[DATASEG].imageUsed = 4; // skip the 0 byte, so NULL pointers are fixed up properly - instructionCount = 0; - - for ( i = 0 ; i < numAsmFiles ; i++ ) { - currentFileIndex = i; - currentFileName = asmFileNames[ i ]; - currentFileLine = 0; - report("pass %i: %s\n", passNumber, currentFileName ); - fflush( NULL ); - ptr = asmFiles[i]; - while ( ptr ) { - ptr = ExtractLine( ptr ); - AssembleLine(); - } - } - - // align all segment - for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) { - segment[i].imageUsed = (segment[i].imageUsed + 3) & ~3; - } - if (passNumber == 0) { - sort_symbols(); - } - } - - // reserve the stack in bss - DefineSymbol( "_stackStart", segment[BSSSEG].imageUsed ); - segment[BSSSEG].imageUsed += stackSize; - DefineSymbol( "_stackEnd", segment[BSSSEG].imageUsed ); - - // write the image - WriteVmFile(); - - // write the map file even if there were errors - if( options.writeMapFile ) { - WriteMapFile(); - } -} - - -/* -============= -ParseOptionFile - -============= -*/ -void ParseOptionFile( const char *filename ) { - char expanded[MAX_OS_PATH]; - char *text, *text_p; - - strcpy( expanded, filename ); - DefaultExtension( expanded, ".q3asm" ); - LoadFile( expanded, (void **)&text ); - if ( !text ) { - return; - } - - text_p = text; - - while( ( text_p = COM_Parse( text_p ) ) != 0 ) { - if ( !strcmp( com_token, "-o" ) ) { - // allow output override in option file - text_p = COM_Parse( text_p ); - if ( text_p ) { - strcpy( outputFilename, com_token ); - } - continue; - } - - asmFileNames[ numAsmFiles ] = copystring( com_token ); - numAsmFiles++; - } -} - -/* -============== -main -============== -*/ -int main( int argc, char **argv ) { - int i; - double start, end; - -// _chdir( "/quake3/jccode/cgame/lccout" ); // hack for vc profiler - - if ( argc < 2 ) { - Error("Usage: %s [OPTION]... [FILES]...\n\ -Assemble LCC bytecode assembly to Q3VM bytecode.\n\ -\n\ - -o OUTPUT Write assembled output to file OUTPUT.qvm\n\ - -f LISTFILE Read options and list of files to assemble from LISTFILE\n\ - -b BUCKETS Set symbol hash table to BUCKETS buckets\n\ - -v Verbose compilation report\n\ - -vq3 Produce a qvm file compatible with Q3 1.32b\n\ -", argv[0]); - } - - start = I_FloatTime (); - - // default filename is "q3asm" - strcpy( outputFilename, "q3asm" ); - numAsmFiles = 0; - - for ( i = 1 ; i < argc ; i++ ) { - if ( argv[i][0] != '-' ) { - break; - } - if ( !strcmp( argv[i], "-o" ) ) { - if ( i == argc - 1 ) { - Error( "-o must preceed a filename" ); - } -/* Timbo of Tremulous pointed out -o not working; stock ID q3asm folded in the change. Yay. */ - strcpy( outputFilename, argv[ i+1 ] ); - i++; - continue; - } - - if ( !strcmp( argv[i], "-f" ) ) { - if ( i == argc - 1 ) { - Error( "-f must preceed a filename" ); - } - ParseOptionFile( argv[ i+1 ] ); - i++; - continue; - } - - if (!strcmp(argv[i], "-b")) { - if (i == argc - 1) { - Error("-b requires an argument"); - } - i++; - symtablelen = atoiNoCap(argv[i]); - continue; - } - - if( !strcmp( argv[ i ], "-v" ) ) { -/* Verbosity option added by Timbo, 2002.09.14. -By default (no -v option), q3asm remains silent except for critical errors. -Verbosity turns on all messages, error or not. -Motivation: not wanting to scrollback for pages to find asm error. -*/ - options.verbose = qtrue; - continue; - } - - if( !strcmp( argv[ i ], "-m" ) ) { - options.writeMapFile = qtrue; - continue; - } - - if( !strcmp( argv[ i ], "-vq3" ) ) { - options.vanillaQ3Compatibility = qtrue; - continue; - } - - Error( "Unknown option: %s", argv[i] ); - } - - // the rest of the command line args are asm files - for ( ; i < argc ; i++ ) { - asmFileNames[ numAsmFiles ] = copystring( argv[ i ] ); - numAsmFiles++; - } - - InitTables(); - Assemble(); - - { - symbol_t *s; - - for ( i = 0, s = symbols ; s ; s = s->next, i++ ) /* nop */ ; - - if (options.verbose) - { - report("%d symbols defined\n", i); - hashtable_stats(symtable); - hashtable_stats(optable); - } - } - - end = I_FloatTime (); - report ("%5.0f seconds elapsed\n", end-start); - - return errorCount; -} - |