q3asm.c

来自「quake3工具源码。包括生成bsp文件」· C语言 代码 · 共 1,017 行 · 第 1/2 页

C
1,017
字号
==============
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, &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, &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] );
			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;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?