diff options
Diffstat (limited to 'src/tools/asm')
| -rw-r--r-- | src/tools/asm/README.Id | 10 | ||||
| -rw-r--r-- | src/tools/asm/cmdlib.c | 1141 | ||||
| -rw-r--r-- | src/tools/asm/cmdlib.h | 153 | ||||
| -rw-r--r-- | src/tools/asm/lib.txt | 31 | ||||
| -rw-r--r-- | src/tools/asm/mathlib.h | 95 | ||||
| -rw-r--r-- | src/tools/asm/notes.txt | 16 | ||||
| -rw-r--r-- | src/tools/asm/ops.txt | 132 | ||||
| -rw-r--r-- | src/tools/asm/opstrings.h | 176 | ||||
| -rw-r--r-- | src/tools/asm/q3asm.c | 1647 | 
9 files changed, 3401 insertions, 0 deletions
diff --git a/src/tools/asm/README.Id b/src/tools/asm/README.Id new file mode 100644 index 0000000..adacb50 --- /dev/null +++ b/src/tools/asm/README.Id @@ -0,0 +1,10 @@ +2002-10-25  Timothee Besset <ttimo@idsoftware.com> +If you are looking for a faster version of the q3asm tool, try: +http://www.icculus.org/~phaethon/q3/q3asm-turbo/q3asm-turbo.html + +2001-10-31  Timothee Besset <ttimo@idsoftware.com> +updated from the $/source/q3asm code +modified for portability and use with >= 1.31 mod source release + +the cmdlib.c cmdlib.h mathlib.h qfiles.h have been copied from +$/source/common diff --git a/src/tools/asm/cmdlib.c b/src/tools/asm/cmdlib.c new file mode 100644 index 0000000..bf50346 --- /dev/null +++ b/src/tools/asm/cmdlib.c @@ -0,0 +1,1141 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +=========================================================================== +*/ +// cmdlib.c + +#include "cmdlib.h" +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef WIN32 +#include <direct.h> +#include <windows.h> +#elif defined(NeXT) +#include <libc.h> +#else +#include <unistd.h> +#endif + +#define	BASEDIRNAME	"quake"		// assumed to have a 2 or 3 following +#define PATHSEPERATOR   '/' + +// set these before calling CheckParm +int myargc; +char **myargv; + +char		com_token[1024]; +qboolean	com_eof; + +qboolean		archive; +char			archivedir[1024]; + + +/* +=================== +ExpandWildcards + +Mimic unix command line expansion +=================== +*/ +#define	MAX_EX_ARGC	1024 +int		ex_argc; +char	*ex_argv[MAX_EX_ARGC]; +#ifdef _WIN32 +#include "io.h" +void ExpandWildcards( int *argc, char ***argv ) +{ +	struct _finddata_t fileinfo; +	intptr_t	handle; +	int		i; +	char	filename[1024]; +	char	filebase[1024]; +	char	*path; + +	ex_argc = 0; +	for (i=0 ; i<*argc ; i++) +	{ +		path = (*argv)[i]; +		if ( path[0] == '-' +			|| ( !strstr(path, "*") && !strstr(path, "?") ) ) +		{ +			ex_argv[ex_argc++] = path; +			continue; +		} + +		handle = _findfirst (path, &fileinfo); +		if (handle == -1) +			return; + +		ExtractFilePath (path, filebase); + +		do +		{ +			sprintf (filename, "%s%s", filebase, fileinfo.name); +			ex_argv[ex_argc++] = copystring (filename); +		} while (_findnext( handle, &fileinfo ) != -1); + +		_findclose (handle); +	} + +	*argc = ex_argc; +	*argv = ex_argv; +} +#else +void ExpandWildcards (int *argc, char ***argv) +{ +} +#endif + +#ifdef WIN_ERROR +#include <windows.h> +/* +================= +Error + +For abnormal program terminations in windowed apps +================= +*/ +void Error( const char *error, ... ) +{ +	va_list argptr; +	char	text[1024]; +	char	text2[1024]; +	int		err; + +	err = GetLastError (); + +	va_start (argptr,error); +	vsprintf (text, error,argptr); +	va_end (argptr); + +	sprintf (text2, "%s\nGetLastError() = %i", text, err); +    MessageBox(NULL, text2, "Error", 0 /* MB_OK */ ); + +	exit (1); +} + +#else +/* +================= +Error + +For abnormal program terminations in console apps +================= +*/ +void Error( const char *error, ...) +{ +	va_list argptr; + +	_printf ("\n************ ERROR ************\n"); + +	va_start (argptr,error); +	vprintf (error,argptr); +	va_end (argptr); +	_printf ("\r\n"); + +	exit (1); +} +#endif + +// only printf if in verbose mode +qboolean verbose = qfalse; +void qprintf( const char *format, ... ) { +	va_list argptr; + +	if (!verbose) +		return; + +	va_start (argptr,format); +	vprintf (format,argptr); +	va_end (argptr); + +} + +#ifdef WIN32 +HWND hwndOut = NULL; +qboolean lookedForServer = qfalse; +UINT wm_BroadcastCommand = -1; +#endif + +void _printf( const char *format, ... ) { +	va_list argptr; +  char text[4096]; +#ifdef WIN32 +  ATOM a; +#endif +	va_start (argptr,format); +	vsprintf (text, format, argptr); +	va_end (argptr); + +  printf("%s", text); + +#ifdef WIN32 +  if (!lookedForServer) { +    lookedForServer = qtrue; +    hwndOut = FindWindow(NULL, "Q3Map Process Server"); +    if (hwndOut) { +      wm_BroadcastCommand = RegisterWindowMessage( "Q3MPS_BroadcastCommand" ); +    } +  } +  if (hwndOut) { +    a = GlobalAddAtom(text); +    PostMessage(hwndOut, wm_BroadcastCommand, 0, (LPARAM)a); +  } +#endif +} + + +/* + +qdir will hold the path up to the quake directory, including the slash + +  f:\quake\ +  /raid/quake/ + +gamedir will hold qdir + the game directory (id1, id2, etc) + +  */ + +char		qdir[1024]; +char		gamedir[1024]; +char		writedir[1024]; + +void SetQdirFromPath( const char *path ) +{ +	char	temp[1024]; +	const char	*c; +  const char *sep; +	int		len, count; + +	if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) +	{	// path is partial +		Q_getwd (temp); +		strcat (temp, path); +		path = temp; +	} + +	// search for "quake2" in path + +	len = strlen(BASEDIRNAME); +	for (c=path+strlen(path)-1 ; c != path ; c--) +	{ +		int i; + +		if (!Q_strncasecmp (c, BASEDIRNAME, len)) +		{ +      // +			//strncpy (qdir, path, c+len+2-path); +      // the +2 assumes a 2 or 3 following quake which is not the +      // case with a retail install +      // so we need to add up how much to the next separator +      sep = c + len; +      count = 1; +      while (*sep && *sep != '/' && *sep != '\\') +      { +        sep++; +        count++; +      } +			strncpy (qdir, path, c+len+count-path); +			qprintf ("qdir: %s\n", qdir); +			for ( i = 0; i < strlen( qdir ); i++ ) +			{ +				if ( qdir[i] == '\\' )  +					qdir[i] = '/'; +			} + +			c += len+count; +			while (*c) +			{ +				if (*c == '/' || *c == '\\') +				{ +					strncpy (gamedir, path, c+1-path); + +					for ( i = 0; i < strlen( gamedir ); i++ ) +					{ +						if ( gamedir[i] == '\\' )  +							gamedir[i] = '/'; +					} + +					qprintf ("gamedir: %s\n", gamedir); + +					if ( !writedir[0] ) +						strcpy( writedir, gamedir ); +					else if ( writedir[strlen( writedir )-1] != '/' ) +					{ +						writedir[strlen( writedir )] = '/'; +						writedir[strlen( writedir )+1] = 0; +					} + +					return; +				} +				c++; +			} +			Error ("No gamedir in %s", path); +			return; +		} +	} +	Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path); +} + +char *ExpandArg (const char *path) +{ +	static char full[1024]; + +	if (path[0] != '/' && path[0] != '\\' && path[1] != ':') +	{ +		Q_getwd (full); +		strcat (full, path); +	} +	else +		strcpy (full, path); +	return full; +} + +char *ExpandPath (const char *path) +{ +	static char full[1024]; +	if (!qdir[0]) +		Error ("ExpandPath called without qdir set"); +	if (path[0] == '/' || path[0] == '\\' || path[1] == ':') { +		strcpy( full, path ); +		return full; +	} +	sprintf (full, "%s%s", qdir, path); +	return full; +} + +char *ExpandGamePath (const char *path) +{ +	static char full[1024]; +	if (!qdir[0]) +		Error ("ExpandGamePath called without qdir set"); +	if (path[0] == '/' || path[0] == '\\' || path[1] == ':') { +		strcpy( full, path ); +		return full; +	} +	sprintf (full, "%s%s", gamedir, path); +	return full; +} + +char *ExpandPathAndArchive (const char *path) +{ +	char	*expanded; +	char	archivename[1024]; + +	expanded = ExpandPath (path); + +	if (archive) +	{ +		sprintf (archivename, "%s/%s", archivedir, path); +		QCopyFile (expanded, archivename); +	} +	return expanded; +} + + +char *copystring(const char *s) +{ +	char	*b; +	b = malloc(strlen(s)+1); +	strcpy (b, s); +	return b; +} + + + +/* +================ +I_FloatTime +================ +*/ +double I_FloatTime (void) +{ +	time_t	t; +	 +	time (&t); +	 +	return t; +#if 0 +// more precise, less portable +	struct timeval tp; +	struct timezone tzp; +	static int		secbase; + +	gettimeofday(&tp, &tzp); +	 +	if (!secbase) +	{ +		secbase = tp.tv_sec; +		return tp.tv_usec/1000000.0; +	} +	 +	return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +#endif +} + +void Q_getwd (char *out) +{ +	int i = 0; + +#ifdef WIN32 +   if (_getcwd (out, 256) == NULL) +     strcpy(out, ".");  /* shrug */ +   strcat (out, "\\"); +#else +   if (getcwd (out, 256) == NULL) +     strcpy(out, ".");  /* shrug */ +   strcat (out, "/"); +#endif + +   while ( out[i] != 0 ) +   { +	   if ( out[i] == '\\' ) +		   out[i] = '/'; +	   i++; +   } +} + + +void Q_mkdir (const char *path) +{ +#ifdef WIN32 +	if (_mkdir (path) != -1) +		return; +#else +	if (mkdir (path, 0777) != -1) +		return; +#endif +	if (errno != EEXIST) +		Error ("mkdir %s: %s",path, strerror(errno)); +} + +/* +============ +FileTime + +returns -1 if not present +============ +*/ +int	FileTime (const char *path) +{ +	struct	stat	buf; +	 +	if (stat (path,&buf) == -1) +		return -1; +	 +	return buf.st_mtime; +} + + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ +	int		c; +	int		len; +	 +	len = 0; +	com_token[0] = 0; +	 +	if (!data) +		return NULL; +		 +// skip whitespace +skipwhite: +	while ( (c = *data) <= ' ') +	{ +		if (c == 0) +		{ +			com_eof = qtrue; +			return NULL;			// end of file; +		} +		data++; +	} +	 +// skip // comments +	if (c=='/' && data[1] == '/') +	{ +		while (*data && *data != '\n') +			data++; +		goto skipwhite; +	} +	 + +// handle quoted strings specially +	if (c == '\"') +	{ +		data++; +		do +		{ +			c = *data++; +			if (c=='\"') +			{ +				com_token[len] = 0; +				return data; +			} +			com_token[len] = c; +			len++; +		} while (1); +	} + +// parse single characters +	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') +	{ +		com_token[len] = c; +		len++; +		com_token[len] = 0; +		return data+1; +	} + +// parse a regular word +	do +	{ +		com_token[len] = c; +		data++; +		len++; +		c = *data; +	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') +			break; +	} while (c>32); +	 +	com_token[len] = 0; +	return data; +} + + +int Q_strncasecmp (const char *s1, const char *s2, int n) +{ +	int		c1, c2; +	 +	do +	{ +		c1 = *s1++; +		c2 = *s2++; + +		if (!n--) +			return 0;		// strings are equal until end point +		 +		if (c1 != c2) +		{ +			if (c1 >= 'a' && c1 <= 'z') +				c1 -= ('a' - 'A'); +			if (c2 >= 'a' && c2 <= 'z') +				c2 -= ('a' - 'A'); +			if (c1 != c2) +				return -1;		// strings not equal +		} +	} while (c1); +	 +	return 0;		// strings are equal +} + +int Q_stricmp (const char *s1, const char *s2) +{ +	return Q_strncasecmp (s1, s2, 99999); +} + + +char *strupr (char *start) +{ +	char	*in; +	in = start; +	while (*in) +	{ +		*in = toupper(*in); +		in++; +	} +	return start; +} + +char *strlower (char *start) +{ +	char	*in; +	in = start; +	while (*in) +	{ +		*in = tolower(*in);  +		in++; +	} +	return start; +} + + +/* +============================================================================= + +						MISC FUNCTIONS + +============================================================================= +*/ + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int CheckParm (const char *check) +{ +	int             i; + +	for (i = 1;i<myargc;i++) +	{ +		if ( !Q_stricmp(check, myargv[i]) ) +			return i; +	} + +	return 0; +} + + + +/* +================ +Q_filelength +================ +*/ +int Q_filelength (FILE *f) +{ +	int		pos; +	int		end; + +	pos = ftell (f); +	fseek (f, 0, SEEK_END); +	end = ftell (f); +	fseek (f, pos, SEEK_SET); + +	return end; +} + +#ifdef MAX_PATH +#undef MAX_PATH +#endif +#define MAX_PATH 4096 +static FILE* myfopen(const char* filename, const char* mode) +{ +	char* p; +	char fn[MAX_PATH]; + +	fn[0] = '\0'; +	strncat(fn, filename, sizeof(fn)-1); + +	for(p=fn;*p;++p) if(*p == '\\') *p = '/'; + +	return fopen(fn, mode); +} + + +FILE *SafeOpenWrite (const char *filename) +{ +	FILE	*f; + +	f = myfopen(filename, "wb"); + +	if (!f) +		Error ("Error opening %s: %s",filename,strerror(errno)); + +	return f; +} + +FILE *SafeOpenRead (const char *filename) +{ +	FILE	*f; + +	f = myfopen(filename, "rb"); + +	if (!f) +		Error ("Error opening %s: %s",filename,strerror(errno)); + +	return f; +} + + +void SafeRead (FILE *f, void *buffer, int count) +{ +	if ( fread (buffer, 1, count, f) != (size_t)count) +		Error ("File read failure"); +} + + +void SafeWrite (FILE *f, const void *buffer, int count) +{ +	if (fwrite (buffer, 1, count, f) != (size_t)count) +		Error ("File write failure"); +} + + +/* +============== +FileExists +============== +*/ +qboolean	FileExists (const char *filename) +{ +	FILE	*f; + +	f = myfopen (filename, "r"); +	if (!f) +		return qfalse; +	fclose (f); +	return qtrue; +} + +/* +============== +LoadFile +============== +*/ +int    LoadFile( const char *filename, void **bufferptr ) +{ +	FILE	*f; +	int    length; +	void    *buffer; + +	f = SafeOpenRead (filename); +	length = Q_filelength (f); +	buffer = malloc (length+1); +	((char *)buffer)[length] = 0; +	SafeRead (f, buffer, length); +	fclose (f); + +	*bufferptr = buffer; +	return length; +} + + +/* +============== +LoadFileBlock +- +rounds up memory allocation to 4K boundry +- +============== +*/ +int    LoadFileBlock( const char *filename, void **bufferptr ) +{ +	FILE	*f; +	int    length, nBlock, nAllocSize; +	void    *buffer; + +	f = SafeOpenRead (filename); +	length = Q_filelength (f); +  nAllocSize = length; +  nBlock = nAllocSize % MEM_BLOCKSIZE; +  if ( nBlock > 0) { +    nAllocSize += MEM_BLOCKSIZE - nBlock; +  } +	buffer = malloc (nAllocSize+1); +  memset(buffer, 0, nAllocSize+1); +	SafeRead (f, buffer, length); +	fclose (f); + +	*bufferptr = buffer; +	return length; +} + + +/* +============== +TryLoadFile + +Allows failure +============== +*/ +int    TryLoadFile (const char *filename, void **bufferptr) +{ +	FILE	*f; +	int    length; +	void    *buffer; + +	*bufferptr = NULL; + +	f = myfopen (filename, "rb"); +	if (!f) +		return -1; +	length = Q_filelength (f); +	buffer = malloc (length+1); +	((char *)buffer)[length] = 0; +	SafeRead (f, buffer, length); +	fclose (f); + +	*bufferptr = buffer; +	return length; +} + + +/* +============== +SaveFile +============== +*/ +void    SaveFile (const char *filename, const void *buffer, int count) +{ +	FILE	*f; + +	f = SafeOpenWrite (filename); +	SafeWrite (f, buffer, count); +	fclose (f); +} + + + +void DefaultExtension (char *path, const char *extension) +{ +	char    *src; +// +// if path doesnt have a .EXT, append extension +// (extension should include the .) +// +	src = path + strlen(path) - 1; + +	while (*src != '/' && *src != '\\' && src != path) +	{ +		if (*src == '.') +			return;                 // it has an extension +		src--; +	} + +	strcat (path, extension); +} + + +void DefaultPath (char *path, const char *basepath) +{ +	char    temp[128]; + +	if (path[0] == PATHSEPERATOR) +		return;                   // absolute path location +	strcpy (temp,path); +	strcpy (path,basepath); +	strcat (path,temp); +} + + +void    StripFilename (char *path) +{ +	int             length; + +	length = strlen(path)-1; +	while (length > 0 && path[length] != PATHSEPERATOR) +		length--; +	path[length] = 0; +} + +void    StripExtension (char *path) +{ +	int             length; + +	length = strlen(path)-1; +	while (length > 0 && path[length] != '.') +	{ +		length--; +		if (path[length] == '/') +			return;		// no extension +	} +	if (length) +		path[length] = 0; +} + + +/* +==================== +Extract file parts +==================== +*/ +// FIXME: should include the slash, otherwise +// backing to an empty path will be wrong when appending a slash +void ExtractFilePath (const char *path, char *dest) +{ +	const char    *src; + +	src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// +	while (src != path && *(src-1) != '\\' && *(src-1) != '/') +		src--; + +	memcpy (dest, path, src-path); +	dest[src-path] = 0; +} + +void ExtractFileBase (const char *path, char *dest) +{ +	const char    *src; + +	src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// +	while (src != path && *(src-1) != PATHSEPERATOR) +		src--; + +	while (*src && *src != '.') +	{ +		*dest++ = *src++; +	} +	*dest = 0; +} + +void ExtractFileExtension (const char *path, char *dest) +{ +	const char    *src; + +	src = path + strlen(path) - 1; + +// +// back up until a . or the start +// +	while (src != path && *(src-1) != '.') +		src--; +	if (src == path) +	{ +		*dest = 0;	// no extension +		return; +	} + +	strcpy (dest,src); +} + + +/* +============== +ParseNum / ParseHex +============== +*/ +int ParseHex (const char *hex) +{ +	const char    *str; +	int    num; + +	num = 0; +	str = hex; + +	while (*str) +	{ +		num <<= 4; +		if (*str >= '0' && *str <= '9') +			num += *str-'0'; +		else if (*str >= 'a' && *str <= 'f') +			num += 10 + *str-'a'; +		else if (*str >= 'A' && *str <= 'F') +			num += 10 + *str-'A'; +		else +			Error ("Bad hex number: %s",hex); +		str++; +	} + +	return num; +} + + +int ParseNum (const char *str) +{ +	if (str[0] == '$') +		return ParseHex (str+1); +	if (str[0] == '0' && str[1] == 'x') +		return ParseHex (str+2); +	return atol (str); +} + + + +/* +============================================================================ + +					BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +short   ShortSwap (short l) +{ +	byte    b1,b2; + +	b1 = l&255; +	b2 = (l>>8)&255; + +	return (b1<<8) + b2; +} + +int    LongSwap (int l) +{ +	byte    b1,b2,b3,b4; + +	b1 = l&255; +	b2 = (l>>8)&255; +	b3 = (l>>16)&255; +	b4 = (l>>24)&255; + +	return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +typedef union { +    float	f; +    unsigned int i; +} _FloatByteUnion; + +float FloatSwap (const float *f) { +	_FloatByteUnion out; + +	out.f = *f; +	out.i = LongSwap(out.i); + +	return out.f; +} + +//======================================================= + + +// FIXME: byte swap? + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below...  in other words, the +// CCITT standard CRC used by XMODEM + +#define CRC_INIT_VALUE	0xffff +#define CRC_XOR_VALUE	0x0000 + +static unsigned short crctable[256] = +{ +	0x0000,	0x1021,	0x2042,	0x3063,	0x4084,	0x50a5,	0x60c6,	0x70e7, +	0x8108,	0x9129,	0xa14a,	0xb16b,	0xc18c,	0xd1ad,	0xe1ce,	0xf1ef, +	0x1231,	0x0210,	0x3273,	0x2252,	0x52b5,	0x4294,	0x72f7,	0x62d6, +	0x9339,	0x8318,	0xb37b,	0xa35a,	0xd3bd,	0xc39c,	0xf3ff,	0xe3de, +	0x2462,	0x3443,	0x0420,	0x1401,	0x64e6,	0x74c7,	0x44a4,	0x5485, +	0xa56a,	0xb54b,	0x8528,	0x9509,	0xe5ee,	0xf5cf,	0xc5ac,	0xd58d, +	0x3653,	0x2672,	0x1611,	0x0630,	0x76d7,	0x66f6,	0x5695,	0x46b4, +	0xb75b,	0xa77a,	0x9719,	0x8738,	0xf7df,	0xe7fe,	0xd79d,	0xc7bc, +	0x48c4,	0x58e5,	0x6886,	0x78a7,	0x0840,	0x1861,	0x2802,	0x3823, +	0xc9cc,	0xd9ed,	0xe98e,	0xf9af,	0x8948,	0x9969,	0xa90a,	0xb92b, +	0x5af5,	0x4ad4,	0x7ab7,	0x6a96,	0x1a71,	0x0a50,	0x3a33,	0x2a12, +	0xdbfd,	0xcbdc,	0xfbbf,	0xeb9e,	0x9b79,	0x8b58,	0xbb3b,	0xab1a, +	0x6ca6,	0x7c87,	0x4ce4,	0x5cc5,	0x2c22,	0x3c03,	0x0c60,	0x1c41, +	0xedae,	0xfd8f,	0xcdec,	0xddcd,	0xad2a,	0xbd0b,	0x8d68,	0x9d49, +	0x7e97,	0x6eb6,	0x5ed5,	0x4ef4,	0x3e13,	0x2e32,	0x1e51,	0x0e70, +	0xff9f,	0xefbe,	0xdfdd,	0xcffc,	0xbf1b,	0xaf3a,	0x9f59,	0x8f78, +	0x9188,	0x81a9,	0xb1ca,	0xa1eb,	0xd10c,	0xc12d,	0xf14e,	0xe16f, +	0x1080,	0x00a1,	0x30c2,	0x20e3,	0x5004,	0x4025,	0x7046,	0x6067, +	0x83b9,	0x9398,	0xa3fb,	0xb3da,	0xc33d,	0xd31c,	0xe37f,	0xf35e, +	0x02b1,	0x1290,	0x22f3,	0x32d2,	0x4235,	0x5214,	0x6277,	0x7256, +	0xb5ea,	0xa5cb,	0x95a8,	0x8589,	0xf56e,	0xe54f,	0xd52c,	0xc50d, +	0x34e2,	0x24c3,	0x14a0,	0x0481,	0x7466,	0x6447,	0x5424,	0x4405, +	0xa7db,	0xb7fa,	0x8799,	0x97b8,	0xe75f,	0xf77e,	0xc71d,	0xd73c, +	0x26d3,	0x36f2,	0x0691,	0x16b0,	0x6657,	0x7676,	0x4615,	0x5634, +	0xd94c,	0xc96d,	0xf90e,	0xe92f,	0x99c8,	0x89e9,	0xb98a,	0xa9ab, +	0x5844,	0x4865,	0x7806,	0x6827,	0x18c0,	0x08e1,	0x3882,	0x28a3, +	0xcb7d,	0xdb5c,	0xeb3f,	0xfb1e,	0x8bf9,	0x9bd8,	0xabbb,	0xbb9a, +	0x4a75,	0x5a54,	0x6a37,	0x7a16,	0x0af1,	0x1ad0,	0x2ab3,	0x3a92, +	0xfd2e,	0xed0f,	0xdd6c,	0xcd4d,	0xbdaa,	0xad8b,	0x9de8,	0x8dc9, +	0x7c26,	0x6c07,	0x5c64,	0x4c45,	0x3ca2,	0x2c83,	0x1ce0,	0x0cc1, +	0xef1f,	0xff3e,	0xcf5d,	0xdf7c,	0xaf9b,	0xbfba,	0x8fd9,	0x9ff8, +	0x6e17,	0x7e36,	0x4e55,	0x5e74,	0x2e93,	0x3eb2,	0x0ed1,	0x1ef0 +}; + +void CRC_Init(unsigned short *crcvalue) +{ +	*crcvalue = CRC_INIT_VALUE; +} + +void CRC_ProcessByte(unsigned short *crcvalue, byte data) +{ +	*crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short CRC_Value(unsigned short crcvalue) +{ +	return crcvalue ^ CRC_XOR_VALUE; +} +//============================================================================= + +/* +============ +CreatePath +============ +*/ +void	CreatePath (const char *path) +{ +	const char	*ofs; +	char		c; +	char		dir[1024]; + +#ifdef _WIN32 +	int		olddrive = -1; + +	if ( path[1] == ':' ) +	{ +		olddrive = _getdrive(); +		_chdrive( toupper( path[0] ) - 'A' + 1 ); +	} +#endif + +	if (path[1] == ':') +		path += 2; + +	for (ofs = path+1 ; *ofs ; ofs++) +	{ +		c = *ofs; +		if (c == '/' || c == '\\') +		{	// create the directory +			memcpy( dir, path, ofs - path ); +			dir[ ofs - path ] = 0; +			Q_mkdir( dir ); +		} +	} + +#ifdef _WIN32 +	if ( olddrive != -1 ) +	{ +		_chdrive( olddrive ); +	} +#endif +} + + +/* +============ +QCopyFile + +  Used to archive source files +============ +*/ +void QCopyFile (const char *from, const char *to) +{ +	void	*buffer; +	int		length; + +	length = LoadFile (from, &buffer); +	CreatePath (to); +	SaveFile (to, buffer, length); +	free (buffer); +} diff --git a/src/tools/asm/cmdlib.h b/src/tools/asm/cmdlib.h new file mode 100644 index 0000000..20d06de --- /dev/null +++ b/src/tools/asm/cmdlib.h @@ -0,0 +1,153 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +=========================================================================== +*/ +// cmdlib.h + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#ifdef _MSC_VER +#pragma warning(disable : 4244)     // MIPS +#pragma warning(disable : 4136)     // X86 +#pragma warning(disable : 4051)     // ALPHA + +#pragma warning(disable : 4018)     // signed/unsigned mismatch +#pragma warning(disable : 4305)     // truncate from double to float + +#pragma check_stack(off) + +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <time.h> +#include <stdarg.h> + +#ifdef _MSC_VER + +#pragma intrinsic( memset, memcpy ) + +#endif + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +typedef enum { qfalse, qtrue } qboolean; +typedef unsigned char byte; +#endif + +#define	MAX_OS_PATH		1024 +#define MEM_BLOCKSIZE 4096 + +// the dec offsetof macro doesnt work very well... +#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +char *strupr (char *in); +char *strlower (char *in); +int Q_strncasecmp( const char *s1, const char *s2, int n ); +int Q_stricmp( const char *s1, const char *s2 ); +void Q_getwd( char *out ); + +int Q_filelength (FILE *f); +int	FileTime( const char *path ); + +void	Q_mkdir( const char *path ); + +extern	char		qdir[1024]; +extern	char		gamedir[1024]; +extern  char		writedir[1024]; +void SetQdirFromPath( const char *path ); +char *ExpandArg( const char *path );	// from cmd line +char *ExpandPath( const char *path );	// from scripts +char *ExpandGamePath (const char *path); +char *ExpandPathAndArchive( const char *path ); + + +double I_FloatTime( void ); + +void	Error( const char *error, ... ); +int		CheckParm( const char *check ); + +FILE	*SafeOpenWrite( const char *filename ); +FILE	*SafeOpenRead( const char *filename ); +void	SafeRead (FILE *f, void *buffer, int count); +void	SafeWrite (FILE *f, const void *buffer, int count); + +int		LoadFile( const char *filename, void **bufferptr ); +int   LoadFileBlock( const char *filename, void **bufferptr ); +int		TryLoadFile( const char *filename, void **bufferptr ); +void	SaveFile( const char *filename, const void *buffer, int count ); +qboolean	FileExists( const char *filename ); + +void 	DefaultExtension( char *path, const char *extension ); +void 	DefaultPath( char *path, const char *basepath ); +void 	StripFilename( char *path ); +void 	StripExtension( char *path ); + +void 	ExtractFilePath( const char *path, char *dest ); +void 	ExtractFileBase( const char *path, char *dest ); +void	ExtractFileExtension( const char *path, char *dest ); + +int 	ParseNum (const char *str); + +char *COM_Parse (char *data); + +extern	char		com_token[1024]; +extern	qboolean	com_eof; + +char *copystring(const char *s); + + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, byte data); +unsigned short CRC_Value(unsigned short crcvalue); + +void	CreatePath( const char *path ); +void	QCopyFile( const char *from, const char *to ); + +extern	qboolean		archive; +extern	char			archivedir[1024]; + + +extern	qboolean verbose; +void qprintf( const char *format, ... ); +void _printf( const char *format, ... ); + +void ExpandWildcards( int *argc, char ***argv ); + + +// for compression routines +typedef struct +{ +	void	*data; +	int		count, width, height; +} cblock_t; + + +#endif diff --git a/src/tools/asm/lib.txt b/src/tools/asm/lib.txt new file mode 100644 index 0000000..737a030 --- /dev/null +++ b/src/tools/asm/lib.txt @@ -0,0 +1,31 @@ + +strlen +strcasecmp +tolower +strcat +strncpy +strcmp +strcpy +strchr + +vsprintf + +memcpy +memset +rand + +atoi +atof + +abs + +floor +fabs +tan +atan +sqrt +log +cos +sin +atan2 + diff --git a/src/tools/asm/mathlib.h b/src/tools/asm/mathlib.h new file mode 100644 index 0000000..bc63a4d --- /dev/null +++ b/src/tools/asm/mathlib.h @@ -0,0 +1,95 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +=========================================================================== +*/ +#ifndef __MATHLIB__ +#define __MATHLIB__ + +// mathlib.h + +#include <math.h> + +#ifdef DOUBLEVEC_T +typedef double vec_t; +#else +typedef float vec_t; +#endif +typedef vec_t vec2_t[3]; +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; + +#define	SIDE_FRONT		0 +#define	SIDE_ON			2 +#define	SIDE_BACK		1 +#define	SIDE_CROSS		-2 + +#define	Q_PI	3.14159265358979323846 +#define DEG2RAD( a ) ( ( (a) * Q_PI ) / 180.0F ) +#define RAD2DEG( a ) ( ( (a) * 180.0f ) / Q_PI ) + +extern vec3_t vec3_origin; + +#define	EQUAL_EPSILON	0.001 + +// plane types are used to speed some tests +// 0-2 are axial planes +#define	PLANE_X			0 +#define	PLANE_Y			1 +#define	PLANE_Z			2 +#define	PLANE_NON_AXIAL	3 + +qboolean VectorCompare( const vec3_t v1, const vec3_t v2 ); + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) +#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} +#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} +#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} +#define VectorScale(a,b,c) {c[0]=b*a[0];c[1]=b*a[1];c[2]=b*a[2];} +#define VectorClear(x) {x[0] = x[1] = x[2] = 0;} +#define	VectorNegate(x) {x[0]=-x[0];x[1]=-x[1];x[2]=-x[2];} +void Vec10Copy( vec_t *in, vec_t *out ); + +vec_t Q_rint (vec_t in); +vec_t _DotProduct (vec3_t v1, vec3_t v2); +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); +void _VectorCopy (vec3_t in, vec3_t out); +void _VectorScale (vec3_t v, vec_t scale, vec3_t out); + +double VectorLength( const vec3_t v ); + +void VectorMA( const vec3_t va, double scale, const vec3_t vb, vec3_t vc ); + +void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ); +vec_t VectorNormalize( const vec3_t in, vec3_t out ); +vec_t ColorNormalize( const vec3_t in, vec3_t out ); +void VectorInverse (vec3_t v); + +void ClearBounds (vec3_t mins, vec3_t maxs); +void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ); + +qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ); + +void NormalToLatLong( const vec3_t normal, byte bytes[2] ); + +int	PlaneTypeForNormal (vec3_t normal); + +#endif diff --git a/src/tools/asm/notes.txt b/src/tools/asm/notes.txt new file mode 100644 index 0000000..63297f3 --- /dev/null +++ b/src/tools/asm/notes.txt @@ -0,0 +1,16 @@ + +don't do any paramter conversion (double to float, etc) + + + +Why? + +Security. +Portability. + +It may be more aproachable. + +can still use regular dlls for development purposes + +lcc +q3asm diff --git a/src/tools/asm/ops.txt b/src/tools/asm/ops.txt new file mode 100644 index 0000000..e897f49 --- /dev/null +++ b/src/tools/asm/ops.txt @@ -0,0 +1,132 @@ +CNSTF, +CNSTI, +CNSTP, +CNSTU, + +ARGB, +ARGF, +ARGI, +ARGP, +ARGU, + +ASGNB, +ASGNF, +ASGNI, +ASGNP, +ASGNU, + +INDIRB, +INDIRF, +INDIRI, +INDIRP, +INDIRU, + +CVFF, +CVFI, + +CVIF, +CVII, +CVIU, + +CVPU, + +CVUI, +CVUP, +CVUU, + +NEGF, +NEGI, + +CALLB, +CALLF, +CALLI, +CALLP, +CALLU, +CALLV, + +RETF, +RETI, +RETP, +RETU, +RETV, + +ADDRGP, + +ADDRFP, + +ADDRLP, + +ADDF, +ADDI, +ADDP, +ADDU, + +SUBF, +SUBI, +SUBP, +SUBU, + +LSHI, +LSHU, + +MODI, +MODU, + +RSHI, +RSHU, + +BANDI, +BANDU, + +BCOMI, +BCOMU, + +BORI, +BORU, + +BXORI, +BXORU, + +DIVF, +DIVI, +DIVU, + +MULF, +MULI, +MULU, + +EQF, +EQI, +EQU, + +GEF, +GEI, +GEU, + +GTF, +GTI, +GTU, + +LEF, +LEI, +LEU, + +LTF, +LTI, +LTU, + +NEF, +NEI, +NEU, + +JUMPV, + +LABELV, + +LOADB, +LOADF, +LOADI, +LOADP, +LOADU, + + diff --git a/src/tools/asm/opstrings.h b/src/tools/asm/opstrings.h new file mode 100644 index 0000000..d2c3210 --- /dev/null +++ b/src/tools/asm/opstrings.h @@ -0,0 +1,176 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +=========================================================================== +*/ +{ "BREAK", OP_BREAK }, + +{ "CNSTF4", OP_CONST }, +{ "CNSTI4", OP_CONST }, +{ "CNSTP4", OP_CONST }, +{ "CNSTU4", OP_CONST }, + +{ "CNSTI2", OP_CONST }, +{ "CNSTU2", OP_CONST }, + +{ "CNSTI1", OP_CONST }, +{ "CNSTU1", OP_CONST }, + +//{ "ARGB", OP_ARG }, +//{ "ARGF", OP_ARG }, +//{ "ARGI", OP_ARG }, +//{ "ARGP", OP_ARG }, +//{ "ARGU", OP_ARG }, + +{ "ASGNB", 	OP_BLOCK_COPY }, +{ "ASGNF4", OP_STORE4 }, +{ "ASGNI4", OP_STORE4 }, +{ "ASGNP4", OP_STORE4 }, +{ "ASGNU4", OP_STORE4 }, + +{ "ASGNI2", OP_STORE2 }, +{ "ASGNU2", OP_STORE2 }, + +{ "ASGNI1", OP_STORE1 }, +{ "ASGNU1", OP_STORE1 }, + +{ "INDIRB", OP_IGNORE },	// block copy deals with this +{ "INDIRF4", OP_LOAD4 }, +{ "INDIRI4", OP_LOAD4 }, +{ "INDIRP4", OP_LOAD4 }, +{ "INDIRU4", OP_LOAD4 }, + +{ "INDIRI2", OP_LOAD2 }, +{ "INDIRU2", OP_LOAD2 }, + +{ "INDIRI1", OP_LOAD1 }, +{ "INDIRU1", OP_LOAD1 }, + +{ "CVFF4", OP_UNDEF }, +{ "CVFI4", OP_CVFI }, + +{ "CVIF4", OP_CVIF }, +{ "CVII4", OP_SEX8 },	// will be either SEX8 or SEX16 +{ "CVII1", OP_IGNORE }, +{ "CVII2", OP_IGNORE }, +{ "CVIU4", OP_IGNORE }, + +{ "CVPU4", OP_IGNORE }, + +{ "CVUI4", OP_IGNORE }, +{ "CVUP4", OP_IGNORE }, +{ "CVUU4", OP_IGNORE }, + +{ "CVUU1", OP_IGNORE }, + +{ "NEGF4", OP_NEGF }, +{ "NEGI4", OP_NEGI }, + +//{ "CALLB", OP_UNDEF }, +//{ "CALLF", OP_UNDEF }, +//{ "CALLI", OP_UNDEF }, +//{ "CALLP", OP_UNDEF }, +//{ "CALLU", OP_UNDEF }, +//{ "CALLV", OP_CALL }, + +//{ "RETF", OP_UNDEF }, +//{ "RETI", OP_UNDEF }, +//{ "RETP", OP_UNDEF }, +//{ "RETU", OP_UNDEF }, +//{ "RETV", OP_UNDEF }, + +{ "ADDRGP4", OP_CONST }, + +//{ "ADDRFP", OP_PARM }, +//{ "ADDRLP", OP_LOCAL }, + +{ "ADDF4", OP_ADDF }, +{ "ADDI4", OP_ADD }, +{ "ADDP4", OP_ADD }, +{ "ADDP", OP_ADD }, +{ "ADDU4", OP_ADD }, + +{ "SUBF4", OP_SUBF }, +{ "SUBI4", OP_SUB }, +{ "SUBP4", OP_SUB }, +{ "SUBU4", OP_SUB }, + +{ "LSHI4", OP_LSH }, +{ "LSHU4", OP_LSH }, + +{ "MODI4", OP_MODI }, +{ "MODU4", OP_MODU }, + +{ "RSHI4", OP_RSHI }, +{ "RSHU4", OP_RSHU }, + +{ "BANDI4", OP_BAND }, +{ "BANDU4", OP_BAND }, + +{ "BCOMI4", OP_BCOM }, +{ "BCOMU4", OP_BCOM }, + +{ "BORI4", OP_BOR }, +{ "BORU4", OP_BOR }, + +{ "BXORI4", OP_BXOR }, +{ "BXORU4", OP_BXOR }, + +{ "DIVF4", OP_DIVF }, +{ "DIVI4", OP_DIVI }, +{ "DIVU4", OP_DIVU }, + +{ "MULF4", OP_MULF }, +{ "MULI4", OP_MULI }, +{ "MULU4", OP_MULU }, + +{ "EQF4", OP_EQF }, +{ "EQI4", OP_EQ }, +{ "EQU4", OP_EQ }, + +{ "GEF4", OP_GEF }, +{ "GEI4", OP_GEI }, +{ "GEU4", OP_GEU }, + +{ "GTF4", OP_GTF }, +{ "GTI4", OP_GTI }, +{ "GTU4", OP_GTU }, + +{ "LEF4", OP_LEF }, +{ "LEI4", OP_LEI }, +{ "LEU4", OP_LEU }, + +{ "LTF4", OP_LTF }, +{ "LTI4", OP_LTI }, +{ "LTU4", OP_LTU }, + +{ "NEF4", OP_NEF }, +{ "NEI4", OP_NE }, +{ "NEU4", OP_NE }, + +{ "JUMPV", OP_JUMP }, + +{ "LOADB4", OP_UNDEF }, +{ "LOADF4", OP_UNDEF }, +{ "LOADI4", OP_UNDEF }, +{ "LOADP4", OP_UNDEF }, +{ "LOADU4", OP_UNDEF }, + + diff --git a/src/tools/asm/q3asm.c b/src/tools/asm/q3asm.c new file mode 100644 index 0000000..c42b9ac --- /dev/null +++ b/src/tools/asm/q3asm.c @@ -0,0 +1,1647 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +=========================================================================== +*/ + +#include "../../qcommon/q_platform.h" +#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,	// pseudo-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 ]; + + + +static int vreport (const char* fmt, va_list vp) +{ +  if (options.verbose != qtrue) +      return 0; +  return vprintf(fmt, vp); +} + +static 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 */ + +static void hashtable_init (hashtable_t *H, int buckets) +{ +  H->buckets = buckets; +  H->table = calloc(H->buckets, sizeof(*(H->table))); +  return; +} + +static 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. */ + +static 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; +} + +static hashchain_t *hashtable_get (hashtable_t *H, int hashvalue) +{ +  hashvalue = (abs(hashvalue) % H->buckets); +  return (H->table[hashvalue]); +} + +static 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. */ +static 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. */ +static 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 +*/ +static 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. +*/ +static 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. */ +static 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 +============ +*/ +static void CodeError( char *fmt, ... ) { +	va_list		argptr; + +	errorCount++; + +	fprintf( stderr, "%s:%i ", currentFileName, currentFileLine ); + +	va_start( argptr,fmt ); +	vfprintf( stderr, fmt, argptr ); +	va_end( argptr ); +} + +/* +============ +EmitByte +============ +*/ +static void EmitByte( segment_t *seg, int v ) { +	if ( seg->imageUsed >= MAX_IMAGE ) { +		Error( "MAX_IMAGE" ); +	} +	seg->image[ seg->imageUsed ] = v; +	seg->imageUsed++; +} + +/* +============ +EmitInt +============ +*/ +static 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 +============ +*/ +static 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 +============ +*/ +static 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 +=============== +*/ +static 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 +============== +*/ +static 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 +============== +*/ +static int ParseValue( void ) { +	Parse(); +	return atoiNoCap( token ); +} + + +/* +============== +ParseExpression +============== +*/ +static 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. +============== +*/ +static 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 + +============== +*/ +static 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 +============== +*/ +static 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 +=============== +*/ +static 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 ); + +#ifdef Q3_BIG_ENDIAN +	{ +		int i; + +		// byte swap the header +		for ( i = 0 ; i < sizeof( vmHeader_t ) / 4 ; i++ ) { +			((int *)&header)[i] = LittleLong( ((int *)&header)[i] ); +		} +	} +#endif + +	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 +=============== +*/ +static 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 + +============= +*/ +static 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++; +	} +} + +static void ShowHelp( char *argv0 ) { +	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.q3asm\n\ +  -b BUCKETS     Set symbol hash table to BUCKETS buckets\n\ +  -m             Generate a mapfile for each OUTPUT.qvm\n\ +  -v             Verbose compilation report\n\ +  -vq3           Produce a qvm file compatible with Q3 1.32b\n\ +  -h --help -?   Show this help\n\ +", argv0); +} + +/* +============== +main +============== +*/ +int main( int argc, char **argv ) { +	int			i; +	double		start, end; + +//	_chdir( "/quake3/jccode/cgame/lccout" );	// hack for vc profiler + +	if ( argc < 2 ) { +		ShowHelp( 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 ], "-h" ) ||  +		    !strcmp( argv[ i ], "--help" ) || +		    !strcmp( argv[ i ], "-?") ) { +			ShowHelp( argv[0] ); +		} + +		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++; +	} +	// In some case it Segfault without this check +	if ( numAsmFiles == 0 ) { +		Error( "No file to assemble\n" ); +	} + +	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; +} +  | 
