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 + -
显示快捷键?