1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048 |
- /*
- ===========================================================================
- 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 Foobar; 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 "qfiles.h"
- /* MSVC-ism fix. */
- #define atoi(s) strtoul(s,NULL,10)
- 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
- 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;
- segment_t segment[NUM_SEGMENTS];
- segment_t *currentSegment;
- int passNumber;
- int numSymbols;
- int errorCount;
- symbol_t *symbols;
- symbol_t *lastSymbol;
- #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 ];
- /*
- =============
- HashString
- =============
- */
- int HashString( char *s ) {
- int v = 0;
- while ( *s ) {
- v += *s;
- s++;
- }
- return v;
- }
- /*
- ============
- CodeError
- ============
- */
- void CodeError( char *fmt, ... ) {
- va_list argptr;
- errorCount++;
- printf( "%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 ) {
- symbol_t *s, *after;
- char expanded[MAX_LINE_LENGTH];
- int hash;
- if ( passNumber == 1 ) {
- return;
- }
-
- // TTimo
- // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=381
- // as a security, bail out if vmMain entry point is not first
- if (!Q_stricmp(sym, "vmMain"))
- if (value)
- Error( "vmMain must be the first symbol in the qvm (got offset %d)\n", value );
- // add the file prefix to local symbols to guarantee unique
- if ( sym[0] == '$' ) {
- sprintf( expanded, "%s_%i", sym, currentFileIndex );
- sym = expanded;
- }
- hash = HashString( sym );
- for ( s = symbols ; s ; s = s->next ) {
- if ( hash == s->hash && !strcmp( sym, s->name ) ) {
- CodeError( "Multiple definitions for %s\n", sym );
- return;
- }
- }
- s = malloc( sizeof( *s ) );
- s->name = copystring( sym );
- s->hash = hash;
- s->value = value;
- s->segment = currentSegment;
- lastSymbol = s; /* for the move-to-lit-segment byteswap hack */
- // insert it in order
- if ( !symbols || s->value < symbols->value ) {
- s->next = symbols;
- symbols = s;
- return;
- }
- for ( after = symbols ; after->next && after->next->value < value ; after = after->next ) {
- }
- s->next = after->next;
- after->next = s;
- }
- /*
- ============
- LookupSymbol
- Symbols can only be evaluated on pass 1
- ============
- */
- int LookupSymbol( char *sym ) {
- symbol_t *s;
- char expanded[MAX_LINE_LENGTH];
- int hash;
- 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 );
- for ( s = symbols ; s ; s = s->next ) {
- 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 ) {
- int i;
- currentFileLine++;
- lineParseOffset = 0;
- token[0] = 0;
- if ( data[0] == 0 ) {
- lineBuffer[0] = 0;
- return NULL;
- }
- for ( i = 0 ; i < MAX_LINE_LENGTH ; i++ ) {
- if ( data[i] == 0 || data[i] == '\n' ) {
- break;
- }
- }
- if ( i == MAX_LINE_LENGTH ) {
- CodeError( "MAX_LINE_LENGTH" );
- return data;
- }
- memcpy( lineBuffer, data, i );
- lineBuffer[i] = 0;
- data += i;
- if ( data[0] == '\n' ) {
- data++;
- }
- return data;
- }
- /*
- ==============
- Parse
- Parse a token out of linebuffer
- ==============
- */
- qboolean Parse( void ) {
- int c;
- int len;
-
- len = 0;
- token[0] = 0;
-
- // skip whitespace
- while ( lineBuffer[ lineParseOffset ] <= ' ' ) {
- if ( lineBuffer[ lineParseOffset ] == 0 ) {
- return qfalse;
- }
- lineParseOffset++;
- }
- // skip ; comments
- c = lineBuffer[ lineParseOffset ];
- if ( c == ';' ) {
- return qfalse;
- }
-
- // parse a regular word
- do {
- token[len] = c;
- len++;
- lineParseOffset++;
- c = lineBuffer[ lineParseOffset ];
- } while (c>32);
-
- token[len] = 0;
- return qtrue;
- }
- /*
- ==============
- ParseValue
- ==============
- */
- int ParseValue( void ) {
- Parse();
- return atoi( token );
- }
- /*
- ==============
- ParseExpression
- ==============
- */
- int ParseExpression(void) {
- int i, j;
- char sym[MAX_LINE_LENGTH];
- int v;
- if ( token[0] == '-' ) {
- i = 1;
- } else {
- i = 0;
- }
- for ( ; i < MAX_LINE_LENGTH ; i++ ) {
- if ( token[i] == '+' || token[i] == '-' || token[i] == 0 ) {
- break;
- }
- }
- memcpy( sym, token, i );
- sym[i] = 0;
- if ( ( sym[0] >= '0' && sym[0] <= '9' ) || sym[0] == '-' ) {
- v = atoi( sym );
- } else {
- v = LookupSymbol( sym );
- }
- // 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;
- if ( token[i] == '+' ) {
- v += atoi( sym );
- }
- if ( token[i] == '-' ) {
- v -= atoi( sym );
- }
- 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;
- }
- }
- /*
- ==============
- AssembleLine
- ==============
- */
- void AssembleLine( void ) {
- int v, v2;
- int i;
- int hash;
- Parse();
- if ( !token[0] ) {
- return;
- }
- hash = HashString( token );
- for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) {
- if ( hash == opcodesHash[i] && !strcmp( token, sourceOps[i].name ) ) {
- int opcode;
- int expression;
- if ( sourceOps[i].opcode == OP_UNDEF ) {
- CodeError( "Undefined opcode: %s\n", token );
- }
- if ( sourceOps[i].opcode == OP_IGNORE ) {
- return; // we ignore most conversions
- }
- // sign extensions need to check next parm
- opcode = sourceOps[i].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] && sourceOps[i].opcode != OP_CVIF
- && sourceOps[i].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;
- }
- }
- // call instructions reset currentArgOffset
- if ( !strncmp( token, "CALL", 4 ) ) {
- EmitByte( &segment[CODESEG], OP_CALL );
- instructionCount++;
- currentArgOffset = 0;
- return;
- }
- // arg is converted to a reversed store
- if ( !strncmp( token, "ARG", 3 ) ) {
- EmitByte( &segment[CODESEG], OP_ARG );
- instructionCount++;
- if ( 8 + currentArgOffset >= 256 ) {
- CodeError( "currentArgOffset >= 256" );
- return;
- }
- EmitByte( &segment[CODESEG], 8 + currentArgOffset );
- currentArgOffset += 4;
- return;
- }
- // ret just leaves something on the op stack
- if ( !strncmp( token, "RET", 3 ) ) {
- EmitByte( &segment[CODESEG], OP_LEAVE );
- instructionCount++;
- EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
- return;
- }
- // pop is needed to discard the return value of
- // a function
- if ( !strncmp( token, "pop", 3 ) ) {
- EmitByte( &segment[CODESEG], OP_POP );
- instructionCount++;
- return;
- }
- // address of a parameter is converted to OP_LOCAL
- if ( !strncmp( token, "ADDRF", 5 ) ) {
- instructionCount++;
- Parse();
- v = ParseExpression();
- v = 16 + currentArgs + currentLocals + v;
- EmitByte( &segment[CODESEG], OP_LOCAL );
- EmitInt( &segment[CODESEG], v );
- return;
- }
- // address of a local is converted to OP_LOCAL
- if ( !strncmp( token, "ADDRL", 5 ) ) {
- instructionCount++;
- Parse();
- v = ParseExpression();
- v = 8 + currentArgs + v;
- EmitByte( &segment[CODESEG], OP_LOCAL );
- EmitInt( &segment[CODESEG], v );
- return;
- }
- if ( !strcmp( token, "proc" ) ) {
- char name[1024];
- 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;
- }
- if ( !strcmp( token, "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;
- }
- if ( !strcmp( token, "address" ) ) {
- Parse();
- v = ParseExpression();
- HackToSegment( DATASEG );
- EmitInt( currentSegment, v );
- return;
- }
- if ( !strcmp( token, "export" ) ) {
- return;
- }
- if ( !strcmp( token, "import" ) ) {
- return;
- }
- if ( !strcmp( token, "code" ) ) {
- currentSegment = &segment[CODESEG];
- return;
- }
- if ( !strcmp( token, "bss" ) ) {
- currentSegment = &segment[BSSSEG];
- return;
- }
- if ( !strcmp( token, "data" ) ) {
- currentSegment = &segment[DATASEG];
- return;
- }
- if ( !strcmp( token, "lit" ) ) {
- currentSegment = &segment[LITSEG];
- return;
- }
- if ( !strcmp( token, "line" ) ) {
- return;
- }
- if ( !strcmp( token, "file" ) ) {
- return;
- }
- if ( !strcmp( token, "equ" ) ) {
- char name[1024];
- Parse();
- strcpy( name, token );
- Parse();
- DefineSymbol( name, atoi(token) );
- return;
- }
- if ( !strcmp( token, "align" ) ) {
- v = ParseValue();
- currentSegment->imageUsed = (currentSegment->imageUsed + v - 1 ) & ~( v - 1 );
- return;
- }
- if ( !strcmp( token, "skip" ) ) {
- v = ParseValue();
- currentSegment->imageUsed += v;
- return;
- }
- if ( !strcmp( token, "byte" ) ) {
- v = ParseValue();
- v2 = ParseValue();
- if ( v == 1 ) {
- HackToSegment( LITSEG );
- } else if ( v == 4 ) {
- HackToSegment( DATASEG );
- } else if ( v == 2 ) {
- CodeError( "16 bit initialized data not supported" );
- }
- // emit little endien
- for ( i = 0 ; i < v ; i++ ) {
- EmitByte( currentSegment, v2 );
- v2 >>= 8;
- }
- return;
- }
- // 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
- if ( !strncmp( token, "LABEL", 5 ) ) {
- Parse();
- if ( currentSegment == &segment[CODESEG] ) {
- DefineSymbol( token, instructionCount );
- } else {
- DefineSymbol( token, currentSegment->imageUsed );
- }
- return;
- }
- CodeError( "Unknown token: %s\n", token );
- }
- /*
- ==============
- InitTables
- ==============
- */
- void InitTables( void ) {
- int i;
- for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) {
- opcodesHash[i] = HashString( sourceOps[i].name );
- }
- }
- /*
- ==============
- WriteMapFile
- ==============
- */
- void WriteMapFile( void ) {
- FILE *f;
- symbol_t *s;
- char imageName[MAX_OS_PATH];
- int seg;
- strcpy( imageName, outputFilename );
- StripExtension( imageName );
- strcat( imageName, ".map" );
- printf( "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;
- printf( "%i total errors\n", errorCount );
- strcpy( imageName, outputFilename );
- StripExtension( imageName );
- strcat( imageName, ".qvm" );
- remove( imageName );
- printf( "code segment: %7i\n", segment[CODESEG].imageUsed );
- printf( "data segment: %7i\n", segment[DATASEG].imageUsed );
- printf( "lit segment: %7i\n", segment[LITSEG].imageUsed );
- printf( "bss segment: %7i\n", segment[BSSSEG].imageUsed );
- printf( "instruction count: %i\n", instructionCount );
- if ( errorCount != 0 ) {
- printf( "Not writing a file due to errors\n" );
- return;
- }
- header.vmMagic = VM_MAGIC;
- header.instructionCount = instructionCount;
- header.codeOffset = sizeof( header );
- 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;
- printf( "Writing to %s\n", imageName );
- CreatePath( imageName );
- f = SafeOpenWrite( imageName );
- SafeWrite( f, &header, sizeof( header ) );
- SafeWrite( f, &segment[CODESEG].image, segment[CODESEG].imageUsed );
- SafeWrite( f, &segment[DATASEG].image, segment[DATASEG].imageUsed );
- SafeWrite( f, &segment[LITSEG].image, segment[LITSEG].imageUsed );
- fclose( f );
- }
- /*
- ===============
- Assemble
- ===============
- */
- void Assemble( void ) {
- int i;
- char filename[MAX_OS_PATH];
- char *ptr;
- printf( "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;
- 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;
- printf("pass %i: %s\n", passNumber, currentFileName );
- 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;
- }
- }
- // 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
- 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: q3asm [-o output] <files> or q3asm -f <listfile>\n" );
- }
- start = I_FloatTime ();
- InitTables();
- // 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" );
- }
- 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;
- }
- 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++;
- }
- Assemble();
- end = I_FloatTime ();
- printf ("%5.0f seconds elapsed\n", end-start);
- return 0;
- }
|