q3asm.c

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

C
1,017
字号

#include "cmdlib.h"
#include "mathlib.h"
#include "qfiles.h"

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;

	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;
	}

	// 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;
	}
}

/*

⌨️ 快捷键说明

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