From 304d4258d3a49488f570b8ad71931faa7e5d40ba Mon Sep 17 00:00:00 2001 From: Tim Angus Date: Sat, 3 Oct 2009 12:31:59 +0000 Subject: * Merge ioq3-r1498, by popular demand --- Makefile | 150 +-- src/client/cl_cgame.c | 8 +- src/client/cl_cin.c | 81 +- src/client/cl_console.c | 20 +- src/client/cl_curl.c | 2 +- src/client/cl_input.c | 2 +- src/client/cl_keys.c | 71 +- src/client/cl_main.c | 74 +- src/client/cl_ui.c | 8 +- src/client/snd_openal.c | 2 +- src/libspeex/config.h | 20 + src/qcommon/cm_load.c | 19 +- src/qcommon/cm_trace.c | 5 +- src/qcommon/cmd.c | 76 +- src/qcommon/common.c | 98 +- src/qcommon/cvar.c | 22 + src/qcommon/msg.c | 36 +- src/qcommon/net_ip.c | 40 +- src/qcommon/parse.c | 2 +- src/qcommon/q_math.c | 26 +- src/qcommon/q_shared.c | 188 ++-- src/qcommon/q_shared.h | 11 +- src/qcommon/qcommon.h | 24 +- src/qcommon/unzip.c | 6 +- src/qcommon/vm.c | 72 +- src/qcommon/vm_powerpc.c | 2170 ++++++++++++++++++++++++++++++++++++++++++ src/qcommon/vm_powerpc_asm.c | 1009 ++++++++++++++++++++ src/qcommon/vm_powerpc_asm.h | 156 +++ src/renderer/tr_bsp.c | 13 +- src/renderer/tr_image.c | 14 +- src/renderer/tr_image_bmp.c | 19 +- src/renderer/tr_image_jpg.c | 13 +- src/renderer/tr_image_pcx.c | 27 +- src/renderer/tr_image_png.c | 7 +- src/renderer/tr_image_tga.c | 15 +- src/renderer/tr_model.c | 27 +- src/sdl/sdl_input.c | 74 +- src/server/sv_ccmds.c | 13 + src/server/sv_client.c | 1 + src/server/sv_game.c | 11 +- src/server/sv_main.c | 7 +- src/sys/con_tty.c | 20 +- src/sys/sys_unix.c | 5 +- src/tools/lcc/src/bytecode.c | 14 +- src/tools/lcc/src/c.h | 6 + 45 files changed, 4122 insertions(+), 562 deletions(-) create mode 100644 src/libspeex/config.h create mode 100644 src/qcommon/vm_powerpc.c create mode 100644 src/qcommon/vm_powerpc_asm.c create mode 100644 src/qcommon/vm_powerpc_asm.h diff --git a/Makefile b/Makefile index caa68fae..78c6b33f 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,9 @@ export PLATFORM ifeq ($(COMPILE_ARCH),powerpc) COMPILE_ARCH=ppc endif +ifeq ($(COMPILE_ARCH),powerpc64) + COMPILE_ARCH=ppc64 +endif ifndef ARCH ARCH=$(COMPILE_ARCH) @@ -193,7 +196,7 @@ ifeq ($(wildcard .svn),.svn) endif else ifeq ($(wildcard .git/svn/.metadata),.git/svn/.metadata) - SVN_REV=$(shell LANG=C git-svn info | awk '$$1 == "Revision:" {print $$2; exit 0}') + SVN_REV=$(shell LANG=C git svn info | awk '$$1 == "Revision:" {print $$2; exit 0}') ifneq ($(SVN_REV),) VERSION:=$(VERSION)_SVN$(SVN_REV) endif @@ -267,7 +270,11 @@ ifeq ($(PLATFORM),linux) else ifeq ($(ARCH),ppc) BASE_CFLAGS += -maltivec - HAVE_VM_COMPILED=false + HAVE_VM_COMPILED=true + endif + ifeq ($(ARCH),ppc64) + BASE_CFLAGS += -maltivec + HAVE_VM_COMPILED=true endif endif endif @@ -280,29 +287,29 @@ ifeq ($(PLATFORM),linux) SHLIBCFLAGS=-fPIC SHLIBLDFLAGS=-shared $(LDFLAGS) - THREAD_LDFLAGS=-lpthread - LDFLAGS=-ldl -lm + THREAD_LIBS=-lpthread + LIBS=-ldl -lm - CLIENT_LDFLAGS=$(shell sdl-config --libs) -lGL + CLIENT_LIBS=$(shell sdl-config --libs) -lGL ifeq ($(USE_OPENAL),1) ifneq ($(USE_OPENAL_DLOPEN),1) - CLIENT_LDFLAGS += -lopenal + CLIENT_LIBS += -lopenal endif endif ifeq ($(USE_CURL),1) ifneq ($(USE_CURL_DLOPEN),1) - CLIENT_LDFLAGS += -lcurl + CLIENT_LIBS += -lcurl endif endif ifeq ($(USE_CODEC_VORBIS),1) - CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg + CLIENT_LIBS += -lvorbisfile -lvorbis -logg endif ifeq ($(USE_MUMBLE),1) - CLIENT_LDFLAGS += -lrt + CLIENT_LIBS += -lrt endif ifeq ($(USE_LOCAL_HEADERS),1) @@ -312,11 +319,9 @@ ifeq ($(PLATFORM),linux) ifeq ($(ARCH),x86) # linux32 make ... BASE_CFLAGS += -m32 - LDFLAGS+=-m32 else ifeq ($(ARCH),ppc64) BASE_CFLAGS += -m64 - LDFLAGS += -m64 endif endif @@ -331,7 +336,7 @@ else # ifeq Linux ifeq ($(PLATFORM),darwin) HAVE_VM_COMPILED=true - CLIENT_LDFLAGS= + CLIENT_LIBS= OPTIMIZE= BASE_CFLAGS = -Wall -Wimplicit -Wstrict-prototypes @@ -340,6 +345,9 @@ ifeq ($(PLATFORM),darwin) BASE_CFLAGS += -faltivec OPTIMIZE += -O3 endif + ifeq ($(ARCH),ppc64) + BASE_CFLAGS += -faltivec + endif ifeq ($(ARCH),x86) OPTIMIZE += -march=prescott -mfpmath=sse # x86 vm will crash without -mstackrealign since MMX instructions will be @@ -352,7 +360,7 @@ ifeq ($(PLATFORM),darwin) ifeq ($(USE_OPENAL),1) BASE_CFLAGS += -DUSE_OPENAL ifneq ($(USE_OPENAL_DLOPEN),1) - CLIENT_LDFLAGS += -framework OpenAL + CLIENT_LIBS += -framework OpenAL else BASE_CFLAGS += -DUSE_OPENAL_DLOPEN endif @@ -361,7 +369,7 @@ ifeq ($(PLATFORM),darwin) ifeq ($(USE_CURL),1) BASE_CFLAGS += -DUSE_CURL ifneq ($(USE_CURL_DLOPEN),1) - CLIENT_LDFLAGS += -lcurl + CLIENT_LIBS += -lcurl else BASE_CFLAGS += -DUSE_CURL_DLOPEN endif @@ -369,7 +377,7 @@ ifeq ($(PLATFORM),darwin) ifeq ($(USE_CODEC_VORBIS),1) BASE_CFLAGS += -DUSE_CODEC_VORBIS - CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg + CLIENT_LIBS += -lvorbisfile -lvorbis -logg endif BASE_CFLAGS += -D_THREAD_SAFE=1 @@ -382,7 +390,7 @@ ifeq ($(PLATFORM),darwin) # the file has been modified by each build. LIBSDLMAIN=$(B)/libSDLmain.a LIBSDLMAINSRC=$(LIBSDIR)/macosx/libSDLmain.a - CLIENT_LDFLAGS += -framework Cocoa -framework IOKit -framework OpenGL \ + CLIENT_LIBS += -framework Cocoa -framework IOKit -framework OpenGL \ $(LIBSDIR)/macosx/libSDL-1.2.0.dylib OPTIMIZE += -ffast-math -falign-loops=16 @@ -452,8 +460,9 @@ ifeq ($(PLATFORM),mingw32) BINEXT=.exe - LDFLAGS= -lws2_32 -lwinmm - CLIENT_LDFLAGS = -mwindows -lgdi32 -lole32 -lopengl32 + LIBS= -lws2_32 -lwinmm + CLIENT_LDFLAGS = -mwindows + CLIENT_LIBS = -lgdi32 -lole32 -lopengl32 ifeq ($(USE_CURL),1) BASE_CFLAGS += -DUSE_CURL @@ -461,35 +470,34 @@ ifeq ($(PLATFORM),mingw32) ifneq ($(USE_CURL_DLOPEN),1) ifeq ($(USE_LOCAL_HEADERS),1) BASE_CFLAGS += -DCURL_STATICLIB - CLIENT_LDFLAGS += $(LIBSDIR)/win32/libcurl.a + CLIENT_LIBS += $(LIBSDIR)/win32/libcurl.a else - CLIENT_LDFLAGS += $(CURL_LIBS) + CLIENT_LIBS += $(CURL_LIBS) endif endif endif ifeq ($(USE_CODEC_VORBIS),1) - CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg + CLIENT_LIBS += -lvorbisfile -lvorbis -logg endif ifeq ($(ARCH),x86) # build 32bit BASE_CFLAGS += -m32 - LDFLAGS+=-m32 endif DEBUG_CFLAGS=$(BASE_CFLAGS) -g -O0 RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG $(OPTIMIZE) # libmingw32 must be linked before libSDLmain - CLIENT_LDFLAGS += -lmingw32 + CLIENT_LIBS += -lmingw32 ifeq ($(USE_LOCAL_HEADERS),1) BASE_CFLAGS += -I$(SDLHDIR)/include - CLIENT_LDFLAGS += $(LIBSDIR)/win32/libSDLmain.a \ + CLIENT_LIBS += $(LIBSDIR)/win32/libSDLmain.a \ $(LIBSDIR)/win32/libSDL.dll.a else BASE_CFLAGS += $(SDL_CFLAGS) - CLIENT_LDFLAGS += $(SDL_LIBS) + CLIENT_LIBS += $(SDL_LIBS) endif @@ -547,22 +555,22 @@ ifeq ($(PLATFORM),freebsd) SHLIBCFLAGS=-fPIC SHLIBLDFLAGS=-shared $(LDFLAGS) - THREAD_LDFLAGS=-lpthread + THREAD_LIBS=-lpthread # don't need -ldl (FreeBSD) - LDFLAGS=-lm + LIBS=-lm - CLIENT_LDFLAGS = + CLIENT_LIBS = - CLIENT_LDFLAGS += $(shell sdl-config --libs) -lGL + CLIENT_LIBS += $(shell sdl-config --libs) -lGL ifeq ($(USE_OPENAL),1) ifneq ($(USE_OPENAL_DLOPEN),1) - CLIENT_LDFLAGS += $(THREAD_LDFLAGS) -lopenal + CLIENT_LIBS += $(THREAD_LIBS) -lopenal endif endif ifeq ($(USE_CODEC_VORBIS),1) - CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg + CLIENT_LIBS += -lvorbisfile -lvorbis -logg endif else # ifeq freebsd @@ -604,21 +612,21 @@ ifeq ($(PLATFORM),openbsd) SHLIBCFLAGS=-fPIC SHLIBLDFLAGS=-shared $(LDFLAGS) - THREAD_LDFLAGS=-lpthread - LDFLAGS=-lm + THREAD_LIBS=-lpthread + LIBS=-lm - CLIENT_LDFLAGS = + CLIENT_LIBS = - CLIENT_LDFLAGS += $(shell sdl-config --libs) -lGL + CLIENT_LIBS += $(shell sdl-config --libs) -lGL ifeq ($(USE_OPENAL),1) ifneq ($(USE_OPENAL_DLOPEN),1) - CLIENT_LDFLAGS += $(THREAD_LDFLAGS) -lossaudio -lopenal + CLIENT_LIBS += $(THREAD_LIBS) -lossaudio -lopenal endif endif ifeq ($(USE_CODEC_VORBIS),1) - CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg + CLIENT_LIBS += -lvorbisfile -lvorbis -logg endif else # ifeq openbsd @@ -633,11 +641,11 @@ ifeq ($(PLATFORM),netbsd) ARCH=x86 endif - LDFLAGS=-lm + LIBS=-lm SHLIBEXT=so SHLIBCFLAGS=-fPIC SHLIBLDFLAGS=-shared $(LDFLAGS) - THREAD_LDFLAGS=-lpthread + THREAD_LIBS=-lpthread BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes @@ -672,9 +680,9 @@ ifeq ($(PLATFORM),irix64) SHLIBCFLAGS= SHLIBLDFLAGS=-shared - LDFLAGS=-ldl -lm -lgen + LIBS=-ldl -lm -lgen # FIXME: The X libraries probably aren't necessary? - CLIENT_LDFLAGS=-L/usr/X11/$(LIB) $(shell sdl-config --libs) -lGL \ + CLIENT_LIBS=-L/usr/X11/$(LIB) $(shell sdl-config --libs) -lGL \ -lX11 -lXext -lm else # ifeq IRIX @@ -720,7 +728,6 @@ ifeq ($(PLATFORM),sunos) -falign-functions=2 -fstrength-reduce HAVE_VM_COMPILED=true BASE_CFLAGS += -m32 - LDFLAGS += -m32 BASE_CFLAGS += -I/usr/X11/include/NVIDIA CLIENT_LDFLAGS += -L/usr/X11/lib/NVIDIA -R/usr/X11/lib/NVIDIA endif @@ -738,12 +745,12 @@ ifeq ($(PLATFORM),sunos) SHLIBCFLAGS=-fPIC SHLIBLDFLAGS=-shared $(LDFLAGS) - THREAD_LDFLAGS=-lpthread - LDFLAGS=-lsocket -lnsl -ldl -lm + THREAD_LIBS=-lpthread + LIBS=-lsocket -lnsl -ldl -lm BOTCFLAGS=-O0 - CLIENT_LDFLAGS +=$(shell sdl-config --libs) -lGL + CLIENT_LIBS +=$(shell sdl-config --libs) -lGL else # ifeq sunos @@ -805,7 +812,7 @@ ifeq ($(USE_VOIP),1) ifeq ($(USE_INTERNAL_SPEEX),1) BASE_CFLAGS += -DFLOATING_POINT -DUSE_ALLOCA -I$(SPEEXDIR)/include else - CLIENT_LDFLAGS += -lspeex + CLIENT_LIBS += -lspeex endif endif @@ -925,19 +932,25 @@ targets: makedirs @echo " CC: $(CC)" @echo "" @echo " CFLAGS:" - @for i in $(CFLAGS); \ + -@for i in $(CFLAGS); \ do \ echo " $$i"; \ done @echo "" @echo " LDFLAGS:" - @for i in $(LDFLAGS); \ + -@for i in $(LDFLAGS); \ + do \ + echo " $$i"; \ + done + @echo "" + @echo " LIBS:" + -@for i in $(LIBS); \ do \ echo " $$i"; \ done @echo "" @echo " Output:" - @for i in $(TARGETS); \ + -@for i in $(TARGETS); \ do \ echo " $$i"; \ done @@ -974,6 +987,7 @@ TOOLS_CFLAGS = $(TOOLS_OPTIMIZE) \ -DTEMPDIR=\"$(TEMPDIR)\" -DSYSTEM=\"\" \ -I$(Q3LCCSRCDIR) \ -I$(LBURGDIR) +TOOLS_LIBS = TOOLS_LDFLAGS = ifeq ($(GENERATE_DEPENDENCIES),1) @@ -1006,7 +1020,7 @@ $(B)/tools/lburg/%.o: $(LBURGDIR)/%.c $(LBURG): $(LBURGOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(TOOLS_LDFLAGS) -o $@ $^ + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) Q3RCCOBJ = \ $(B)/tools/rcc/alloc.o \ @@ -1051,7 +1065,7 @@ $(B)/tools/rcc/%.o: $(Q3LCCSRCDIR)/%.c $(Q3RCC): $(Q3RCCOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(TOOLS_LDFLAGS) -o $@ $^ + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) Q3CPPOBJ = \ $(B)/tools/cpp/cpp.o \ @@ -1070,7 +1084,7 @@ $(B)/tools/cpp/%.o: $(Q3CPPDIR)/%.c $(Q3CPP): $(Q3CPPOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(TOOLS_LDFLAGS) -o $@ $^ + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) Q3LCCOBJ = \ $(B)/tools/etc/lcc.o \ @@ -1081,7 +1095,7 @@ $(B)/tools/etc/%.o: $(Q3LCCETCDIR)/%.c $(Q3LCC): $(Q3LCCOBJ) $(Q3RCC) $(Q3CPP) $(echo_cmd) "LD $@" - $(Q)$(CC) $(TOOLS_LDFLAGS) -o $@ $(Q3LCCOBJ) + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $(Q3LCCOBJ) $(TOOLS_LIBS) define DO_Q3LCC $(echo_cmd) "Q3LCC $<" @@ -1113,7 +1127,7 @@ $(B)/tools/asm/%.o: $(Q3ASMDIR)/%.c $(Q3ASM): $(Q3ASMOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(TOOLS_LDFLAGS) -o $@ $^ + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) ############################################################################# @@ -1320,7 +1334,10 @@ ifeq ($(HAVE_VM_COMPILED),true) Q3OBJ += $(B)/client/vm_x86_64.o $(B)/client/vm_x86_64_assembler.o endif ifeq ($(ARCH),ppc) - Q3OBJ += $(B)/client/vm_ppc.o + Q3OBJ += $(B)/client/vm_powerpc.o $(B)/client/vm_powerpc_asm.o + endif + ifeq ($(ARCH),ppc64) + Q3OBJ += $(B)/client/vm_powerpc.o $(B)/client/vm_powerpc_asm.o endif endif @@ -1346,13 +1363,15 @@ Q3POBJ_SMP += \ $(B)/tremulous.$(ARCH)$(BINEXT): $(Q3OBJ) $(Q3POBJ) $(LIBSDLMAIN) $(echo_cmd) "LD $@" - $(Q)$(CC) -o $@ $(Q3OBJ) $(Q3POBJ) $(CLIENT_LDFLAGS) \ - $(LDFLAGS) $(LIBSDLMAIN) + $(Q)$(CC) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) \ + -o $@ $(Q3OBJ) $(Q3POBJ) \ + $(LIBSDLMAIN) $(CLIENT_LIBS) $(LIBS) $(B)/tremulous-smp.$(ARCH)$(BINEXT): $(Q3OBJ) $(Q3POBJ_SMP) $(LIBSDLMAIN) $(echo_cmd) "LD $@" - $(Q)$(CC) -o $@ $(Q3OBJ) $(Q3POBJ_SMP) $(CLIENT_LDFLAGS) \ - $(THREAD_LDFLAGS) $(LDFLAGS) $(LIBSDLMAIN) + $(Q)$(CC) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) $(THREAD_LDFLAGS) \ + -o $@ $(Q3OBJ) $(Q3POBJ_SMP) \ + $(THREAD_LIBS) $(LIBSDLMAIN) $(CLIENT_LIBS) $(LIBS) ifneq ($(strip $(LIBSDLMAIN)),) ifneq ($(strip $(LIBSDLMAINSRC)),) @@ -1420,10 +1439,13 @@ ifeq ($(HAVE_VM_COMPILED),true) Q3DOBJ += $(B)/ded/vm_x86.o endif ifeq ($(ARCH),x86_64) - Q3DOBJ += $(B)/ded/vm_x86_64.o $(B)/client/vm_x86_64_assembler.o + Q3DOBJ += $(B)/ded/vm_x86_64.o $(B)/ded/vm_x86_64_assembler.o endif ifeq ($(ARCH),ppc) - Q3DOBJ += $(B)/ded/vm_ppc.o + Q3DOBJ += $(B)/ded/vm_powerpc.o $(B)/ded/vm_powerpc_asm.o + endif + ifeq ($(ARCH),ppc64) + Q3DOBJ += $(B)/ded/vm_powerpc.o $(B)/ded/vm_powerpc_asm.o endif endif @@ -1440,7 +1462,7 @@ endif $(B)/tremded.$(ARCH)$(BINEXT): $(Q3DOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) -o $@ $(Q3DOBJ) $(LDFLAGS) + $(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(Q3DOBJ) $(LIBS) @@ -1488,7 +1510,7 @@ CGVMOBJ = $(CGOBJ_:%.o=%.asm) $(B)/base/cgame$(ARCH).$(SHLIBEXT): $(CGOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(SHLIBLDFLAGS) -o $@ $(CGOBJ) + $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(CGOBJ) $(B)/base/vm/cgame.qvm: $(CGVMOBJ) $(CGDIR)/cg_syscalls.asm $(Q3ASM) $(echo_cmd) "Q3ASM $@" @@ -1537,7 +1559,7 @@ GVMOBJ = $(GOBJ_:%.o=%.asm) $(B)/base/game$(ARCH).$(SHLIBEXT): $(GOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(SHLIBLDFLAGS) -o $@ $(GOBJ) + $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(GOBJ) $(B)/base/vm/game.qvm: $(GVMOBJ) $(GDIR)/g_syscalls.asm $(Q3ASM) $(echo_cmd) "Q3ASM $@" diff --git a/src/client/cl_cgame.c b/src/client/cl_cgame.c index 6e436316..9a16d3bf 100644 --- a/src/client/cl_cgame.c +++ b/src/client/cl_cgame.c @@ -397,11 +397,9 @@ void CL_ShutdownCGame( void ) { } static int FloatAsInt( float f ) { - int temp; - - *(float *)&temp = f; - - return temp; + floatint_t fi; + fi.f = f; + return fi.i; } /* diff --git a/src/client/cl_cin.c b/src/client/cl_cin.c index 1443937c..7690d627 100644 --- a/src/client/cl_cin.c +++ b/src/client/cl_cin.c @@ -621,7 +621,10 @@ static void decodeCodeBook( byte *input, unsigned short roq_flags ) unsigned short *aptr, *bptr, *cptr, *dptr; long y0,y1,y2,y3,cr,cb; byte *bbptr, *baptr, *bcptr, *bdptr; - unsigned int *iaptr, *ibptr, *icptr, *idptr; + union { + unsigned int *i; + unsigned short *s; + } iaptr, ibptr, icptr, idptr; if (!roq_flags) { two = four = 256; @@ -664,7 +667,7 @@ static void decodeCodeBook( byte *input, unsigned short roq_flags ) VQ2TO4(aptr,bptr,cptr,dptr); } } else if (cinTable[currentHandle].samplesPerPixel==4) { - ibptr = (unsigned int *)bptr; + ibptr.s = bptr; for(i=0;i65536||cinTable[currentHandle].roq_id==0x1084) { Com_DPrintf("roq_size>65536||roq_id==0x1084\n"); diff --git a/src/client/cl_console.c b/src/client/cl_console.c index 0ad397b9..39f13028 100644 --- a/src/client/cl_console.c +++ b/src/client/cl_console.c @@ -233,6 +233,17 @@ void Con_CheckResize (void) con.display = con.current; } +/* +================== +Cmd_CompleteTxtName +================== +*/ +void Cmd_CompleteTxtName( char *args, int argNum ) { + if( argNum == 2 ) { + Field_CompleteFilename( "", "txt", qfalse ); + } +} + /* ================ @@ -255,6 +266,7 @@ void Con_Init (void) { Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); Cmd_AddCommand ("clear", Con_Clear_f); Cmd_AddCommand ("condump", Con_Dump_f); + Cmd_SetCommandCompletionFunc( "condump", Cmd_CompleteTxtName ); } @@ -431,7 +443,7 @@ void Con_DrawSolidConsole( float frac ) { SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL ); // draw the background - y = frac * SCREEN_HEIGHT - 2; + y = frac * SCREEN_HEIGHT; if ( y < 1 ) { y = 0; } @@ -453,10 +465,8 @@ void Con_DrawSolidConsole( float frac ) { i = strlen( Q3_VERSION ); for (x=0 ; x= '0' && n1 <= '9' ) { - n1 -= '0'; - } else if ( n1 >= 'a' && n1 <= 'f' ) { - n1 = n1 - 'a' + 10; - } else { - n1 = 0; - } + if ( strlen( str ) == 4 ) { + int n = Com_HexStrToInt( str ); - n2 = str[3]; - if ( n2 >= '0' && n2 <= '9' ) { - n2 -= '0'; - } else if ( n2 >= 'a' && n2 <= 'f' ) { - n2 = n2 - 'a' + 10; - } else { - n2 = 0; + if ( n >= 0 ) { + return n; } - - return n1 * 16 + n2; } // scan for a text match @@ -1026,6 +1009,50 @@ void Key_KeynameCompletion( void(*callback)(const char *s) ) { callback( keynames[ i ].name ); } +/* +==================== +Key_CompleteUnbind +==================== +*/ +static void Key_CompleteUnbind( char *args, int argNum ) +{ + if( argNum == 2 ) + { + // Skip "unbind " + char *p = Com_SkipTokens( args, 1, " " ); + + if( p > args ) + Field_CompleteKeyname( ); + } +} + +/* +==================== +Key_CompleteBind +==================== +*/ +static void Key_CompleteBind( char *args, int argNum ) +{ + char *p; + + if( argNum == 2 ) + { + // Skip "bind " + p = Com_SkipTokens( args, 1, " " ); + + if( p > args ) + Field_CompleteKeyname( ); + } + else if( argNum >= 3 ) + { + // Skip "bind " + p = Com_SkipTokens( args, 2, " " ); + + if( p > args ) + Field_CompleteCommand( p, qtrue, qtrue ); + } +} + /* =================== CL_InitKeyCommands @@ -1034,7 +1061,9 @@ CL_InitKeyCommands void CL_InitKeyCommands( void ) { // register our functions Cmd_AddCommand ("bind",Key_Bind_f); + Cmd_SetCommandCompletionFunc( "bind", Key_CompleteBind ); Cmd_AddCommand ("unbind",Key_Unbind_f); + Cmd_SetCommandCompletionFunc( "unbind", Key_CompleteUnbind ); Cmd_AddCommand ("unbindall",Key_Unbindall_f); Cmd_AddCommand ("bindlist",Key_Bindlist_f); } diff --git a/src/client/cl_main.c b/src/client/cl_main.c index f91c80b2..3551682f 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -866,6 +866,22 @@ static void CL_WalkDemoExt(char *arg, char *name, int *demofile) } } +/* +==================== +CL_CompleteDemoName +==================== +*/ +static void CL_CompleteDemoName( char *args, int argNum ) +{ + if( argNum == 2 ) + { + char demoExt[ 16 ]; + + Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION ); + Field_CompleteFilename( "demos", demoExt, qtrue ); + } +} + /* ==================== CL_PlayDemo_f @@ -1523,6 +1539,23 @@ void CL_Connect_f( void ) { #define MAX_RCON_MESSAGE 1024 +/* +================== +CL_CompleteRcon +================== +*/ +static void CL_CompleteRcon( char *args, int argNum ) +{ + if( argNum == 2 ) + { + // Skip "rcon " + char *p = Com_SkipTokens( args, 1, " " ); + + if( p > args ) + Field_CompleteCommand( p, qtrue, qtrue ); + } +} + /* ===================== CL_Rcon_f @@ -2126,7 +2159,7 @@ void CL_InitServerInfo( serverInfo_t *server, netadr_t *address ) { CL_ServersResponsePacket =================== */ -void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { +void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extended ) { int i, count, total; netadr_t addresses[MAX_SERVERSPERPACKET]; int numservers; @@ -2149,7 +2182,7 @@ void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { // advance to initial token do { - if(*buffptr == '\\' || *buffptr == '/') + if(*buffptr == '\\' || (extended && *buffptr == '/')) break; buffptr++; @@ -2157,6 +2190,7 @@ void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { while (buffptr + 1 < buffend) { + // IPv4 address if (*buffptr == '\\') { buffptr++; @@ -2169,7 +2203,8 @@ void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { addresses[numservers].type = NA_IP; } - else + // IPv6 address, if it's an extended response + else if (extended && *buffptr == '/') { buffptr++; @@ -2180,7 +2215,11 @@ void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { addresses[numservers].ip6[i] = *buffptr++; addresses[numservers].type = NA_IP6; + addresses[numservers].scope_id = from->scope_id; } + else + // syntax error! + break; // parse out port addresses[numservers].port = (*buffptr++) << 8; @@ -2333,9 +2372,15 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { return; } - // echo request from server + // list of servers sent back by a master server (classic) if ( !Q_strncmp(c, "getserversResponse", 18) ) { - CL_ServersResponsePacket( from, msg ); + CL_ServersResponsePacket( &from, msg, qfalse ); + return; + } + + // list of servers sent back by a master server (extended) + if ( !Q_strncmp(c, "getserversExtResponse", 21) ) { + CL_ServersResponsePacket( &from, msg, qtrue ); return; } @@ -3018,8 +3063,8 @@ void CL_Init( void ) { cl_guidServerUniq = Cvar_Get ("cl_guidServerUniq", "1", CVAR_ARCHIVE); - // 0x7e = ~ and 0x60 = ` - cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "0x7e 0x60", CVAR_ARCHIVE); + // ~ and `, as keys and characters + cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60", CVAR_ARCHIVE); // userinfo Cvar_Get ("name", Sys_GetCurrentUser( ), CVAR_USERINFO | CVAR_ARCHIVE ); @@ -3081,6 +3126,7 @@ void CL_Init( void ) { Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("record", CL_Record_f); Cmd_AddCommand ("demo", CL_PlayDemo_f); + Cmd_SetCommandCompletionFunc( "demo", CL_CompleteDemoName ); Cmd_AddCommand ("cinematic", CL_PlayCinematic_f); Cmd_AddCommand ("stoprecord", CL_StopRecord_f); Cmd_AddCommand ("connect", CL_Connect_f); @@ -3088,6 +3134,7 @@ void CL_Init( void ) { Cmd_AddCommand ("localservers", CL_LocalServers_f); Cmd_AddCommand ("globalservers", CL_GlobalServers_f); Cmd_AddCommand ("rcon", CL_Rcon_f); + Cmd_SetCommandCompletionFunc( "rcon", CL_CompleteRcon ); Cmd_AddCommand ("setenv", CL_Setenv_f ); Cmd_AddCommand ("ping", CL_Ping_f ); Cmd_AddCommand ("serverstatus", CL_ServerStatus_f ); @@ -3557,6 +3604,7 @@ void CL_GlobalServers_f( void ) { netadr_t to; int count, i, masterNum; char command[1024], *masteraddress; + char *cmdname; if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > 4) { @@ -3591,7 +3639,17 @@ void CL_GlobalServers_f( void ) { cls.numglobalservers = -1; cls.pingUpdateSource = AS_GLOBAL; - Com_sprintf( command, sizeof(command), "getservers %s", Cmd_Argv(2) ); + // Use the extended query for IPv6 masters + if (to.type == NA_IP6 || to.type == NA_MULTICAST6) + { + cmdname = "getserversExt " GAMENAME_FOR_MASTER; + + // TODO: test if we only have an IPv6 connection. If it's the case, + // request IPv6 servers only by appending " ipv6" to the command + } + else + cmdname = "getservers"; + Com_sprintf( command, sizeof(command), "%s %s", cmdname, Cmd_Argv(2) ); for (i=3; i < count; i++) { diff --git a/src/client/cl_ui.c b/src/client/cl_ui.c index c0d4e729..49719824 100644 --- a/src/client/cl_ui.c +++ b/src/client/cl_ui.c @@ -631,11 +631,9 @@ FloatAsInt ==================== */ static int FloatAsInt( float f ) { - int temp; - - *(float *)&temp = f; - - return temp; + floatint_t fi; + fi.f = f; + return fi.i; } /* diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c index 5421a7a0..912006d8 100644 --- a/src/client/snd_openal.c +++ b/src/client/snd_openal.c @@ -1921,7 +1921,7 @@ void S_AL_SoundInfo( void ) Com_Printf( " Version: %s\n", qalGetString( AL_VERSION ) ); Com_Printf( " Renderer: %s\n", qalGetString( AL_RENDERER ) ); Com_Printf( " AL Extensions: %s\n", qalGetString( AL_EXTENSIONS ) ); - Com_Printf( " ALC Extensions: %s\n", qalcGetString( NULL, ALC_EXTENSIONS ) ); + Com_Printf( " ALC Extensions: %s\n", qalcGetString( alDevice, ALC_EXTENSIONS ) ); if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) { Com_Printf(" Device: %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER)); diff --git a/src/libspeex/config.h b/src/libspeex/config.h new file mode 100644 index 00000000..abd35f09 --- /dev/null +++ b/src/libspeex/config.h @@ -0,0 +1,20 @@ +// Microsoft version of 'inline' +#define inline __inline + +// Visual Studio support alloca(), but it always align variables to 16-bit +// boundary, while SSE need 128-bit alignment. So we disable alloca() when +// SSE is enabled. +#ifndef _USE_SSE +# define USE_ALLOCA +#endif + +/* Default to floating point */ +#ifndef FIXED_POINT +# define FLOATING_POINT +# define USE_SMALLFT +#else +# define USE_KISS_FFT +#endif + +/* We don't support visibility on Win32 */ +#define EXPORT diff --git a/src/qcommon/cm_load.c b/src/qcommon/cm_load.c index 06a037aa..6291f4d7 100644 --- a/src/qcommon/cm_load.c +++ b/src/qcommon/cm_load.c @@ -714,7 +714,10 @@ Loads in the map and all submodels ================== */ void CM_LoadMap( const char *name, qboolean clientload, int *checksum ) { - int *buf; + union { + int *i; + void *v; + } buf; int i; dheader_t header; int length; @@ -753,19 +756,19 @@ void CM_LoadMap( const char *name, qboolean clientload, int *checksum ) { // load the file // #ifndef BSPC - length = FS_ReadFile( name, (void **)&buf ); + length = FS_ReadFile( name, &buf.v ); #else - length = LoadQuakeFile((quakefile_t *) name, (void **)&buf); + length = LoadQuakeFile((quakefile_t *) name, &buf.v); #endif - if ( !buf ) { + if ( !buf.i ) { Com_Error (ERR_DROP, "Couldn't load %s", name); } - last_checksum = LittleLong (Com_BlockChecksum (buf, length)); + last_checksum = LittleLong (Com_BlockChecksum (buf.i, length)); *checksum = last_checksum; - header = *(dheader_t *)buf; + header = *(dheader_t *)buf.i; for (i=0 ; iname = CopyString( cmd_name ); cmd->function = function; + cmd->complete = NULL; cmd->next = cmd_functions; cmd_functions = cmd; } +/* +============ +Cmd_SetCommandCompletionFunc +============ +*/ +void Cmd_SetCommandCompletionFunc( const char *command, completionFunc_t complete ) { + cmd_function_t *cmd; + + for( cmd = cmd_functions; cmd; cmd = cmd->next ) { + if( !Q_stricmp( command, cmd->name ) ) { + cmd->complete = complete; + } + } +} + /* ============ Cmd_RemoveCommand @@ -668,6 +704,21 @@ void Cmd_CommandCompletion( void(*callback)(const char *s) ) { } } +/* +============ +Cmd_CompleteArgument +============ +*/ +void Cmd_CompleteArgument( const char *command, char *args, int argNum ) { + cmd_function_t *cmd; + + for( cmd = cmd_functions; cmd; cmd = cmd->next ) { + if( !Q_stricmp( command, cmd->name ) && cmd->complete ) { + cmd->complete( args, argNum ); + } + } +} + /* ============ @@ -758,6 +809,17 @@ void Cmd_List_f (void) Com_Printf ("%i commands\n", i); } +/* +================== +Cmd_CompleteCfgName +================== +*/ +void Cmd_CompleteCfgName( char *args, int argNum ) { + if( argNum == 2 ) { + Field_CompleteFilename( "", "cfg", qfalse ); + } +} + /* ============ Cmd_Init @@ -766,7 +828,9 @@ Cmd_Init void Cmd_Init (void) { Cmd_AddCommand ("cmdlist",Cmd_List_f); Cmd_AddCommand ("exec",Cmd_Exec_f); + Cmd_SetCommandCompletionFunc( "exec", Cmd_CompleteCfgName ); Cmd_AddCommand ("vstr",Cmd_Vstr_f); + Cmd_SetCommandCompletionFunc( "vstr", Cvar_CompleteCvarName ); Cmd_AddCommand ("echo",Cmd_Echo_f); Cmd_AddCommand ("wait", Cmd_Wait_f); } diff --git a/src/qcommon/common.c b/src/qcommon/common.c index 7c98d45e..ef9ee76a 100644 --- a/src/qcommon/common.c +++ b/src/qcommon/common.c @@ -2495,6 +2495,7 @@ void Com_Init( char *commandLine ) { Cmd_AddCommand ("quit", Com_Quit_f); Cmd_AddCommand ("changeVectors", MSG_ReportChangeVectors_f ); Cmd_AddCommand ("writeconfig", Com_WriteConfig_f ); + Cmd_SetCommandCompletionFunc( "writeconfig", Cmd_CompleteCfgName ); s = va("%s %s %s", Q3_VERSION, PLATFORM_STRING, __DATE__ ); com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO ); @@ -2993,7 +2994,7 @@ static qboolean Field_Complete( void ) int completionOffset; if( matchCount == 0 ) - return qfalse; + return qtrue; completionOffset = strlen( completionField->buffer ) - strlen( completionString ); @@ -3020,7 +3021,7 @@ static qboolean Field_Complete( void ) Field_CompleteKeyname =============== */ -static void Field_CompleteKeyname( void ) +void Field_CompleteKeyname( void ) { matchCount = 0; shortestMatch[ 0 ] = 0; @@ -3037,7 +3038,7 @@ static void Field_CompleteKeyname( void ) Field_CompleteFilename =============== */ -static void Field_CompleteFilename( const char *dir, +void Field_CompleteFilename( const char *dir, const char *ext, qboolean stripExt ) { matchCount = 0; @@ -3054,11 +3055,10 @@ static void Field_CompleteFilename( const char *dir, Field_CompleteCommand =============== */ -static void Field_CompleteCommand( char *cmd, +void Field_CompleteCommand( char *cmd, qboolean doCommands, qboolean doCvars ) { int completionArgument = 0; - char *p; // Skip leading whitespace and quotes cmd = Com_SkipCharset( cmd, " \"" ); @@ -3100,6 +3100,7 @@ static void Field_CompleteCommand( char *cmd, if( completionArgument > 1 ) { const char *baseCmd = Cmd_Argv( 0 ); + char *p; #ifndef DEDICATED // This should always be true @@ -3108,92 +3109,9 @@ static void Field_CompleteCommand( char *cmd, #endif if( ( p = Field_FindFirstSeparator( cmd ) ) ) - { - // Compound command - Field_CompleteCommand( p + 1, qtrue, qtrue ); - } + Field_CompleteCommand( p + 1, qtrue, qtrue ); // Compound command else - { - // FIXME: all this junk should really be associated with the respective - // commands, instead of being hard coded here - if( ( !Q_stricmp( baseCmd, "map" ) || - !Q_stricmp( baseCmd, "devmap" ) || - !Q_stricmp( baseCmd, "spmap" ) || - !Q_stricmp( baseCmd, "spdevmap" ) ) && - completionArgument == 2 ) - { - Field_CompleteFilename( "maps", "bsp", qtrue ); - } - else if( ( !Q_stricmp( baseCmd, "exec" ) || - !Q_stricmp( baseCmd, "writeconfig" ) ) && - completionArgument == 2 ) - { - Field_CompleteFilename( "", "cfg", qfalse ); - } - else if( !Q_stricmp( baseCmd, "condump" ) && - completionArgument == 2 ) - { - Field_CompleteFilename( "", "txt", qfalse ); - } - else if( ( !Q_stricmp( baseCmd, "toggle" ) || - !Q_stricmp( baseCmd, "vstr" ) || - !Q_stricmp( baseCmd, "set" ) || - !Q_stricmp( baseCmd, "seta" ) || - !Q_stricmp( baseCmd, "setu" ) || - !Q_stricmp( baseCmd, "sets" ) ) && - completionArgument == 2 ) - { - // Skip " " - p = Com_SkipTokens( cmd, 1, " " ); - - if( p > cmd ) - Field_CompleteCommand( p, qfalse, qtrue ); - } -#ifndef DEDICATED - else if( !Q_stricmp( baseCmd, "demo" ) && completionArgument == 2 ) - { - char demoExt[ 16 ]; - - Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION ); - Field_CompleteFilename( "demos", demoExt, qtrue ); - } - else if( !Q_stricmp( baseCmd, "rcon" ) && completionArgument == 2 ) - { - // Skip "rcon " - p = Com_SkipTokens( cmd, 1, " " ); - - if( p > cmd ) - Field_CompleteCommand( p, qtrue, qtrue ); - } - else if( !Q_stricmp( baseCmd, "bind" ) ) - { - if( completionArgument == 2 ) - { - // Skip "bind " - p = Com_SkipTokens( cmd, 1, " " ); - - if( p > cmd ) - Field_CompleteKeyname( ); - } - else if( completionArgument >= 3 ) - { - // Skip "bind " - p = Com_SkipTokens( cmd, 2, " " ); - - if( p > cmd ) - Field_CompleteCommand( p, qtrue, qtrue ); - } - } - else if( !Q_stricmp( baseCmd, "unbind" ) && completionArgument == 2 ) - { - // Skip "unbind " - p = Com_SkipTokens( cmd, 1, " " ); - - if( p > cmd ) - Field_CompleteKeyname( ); - } -#endif - } + Cmd_CompleteArgument( baseCmd, cmd, completionArgument ); } else { diff --git a/src/qcommon/cvar.c b/src/qcommon/cvar.c index cb9f2634..c26ba543 100644 --- a/src/qcommon/cvar.c +++ b/src/qcommon/cvar.c @@ -1053,6 +1053,22 @@ void Cvar_Update( vmCvar_t *vmCvar ) { vmCvar->integer = cv->integer; } +/* +================== +Cvar_CompleteCvarName +================== +*/ +void Cvar_CompleteCvarName( char *args, int argNum ) +{ + if( argNum == 2 ) + { + // Skip " " + char *p = Com_SkipTokens( args, 1, " " ); + + if( p > args ) + Field_CompleteCommand( p, qfalse, qtrue ); + } +} /* ============ @@ -1066,11 +1082,17 @@ void Cvar_Init (void) { Cmd_AddCommand ("print", Cvar_Print_f); Cmd_AddCommand ("toggle", Cvar_Toggle_f); + Cmd_SetCommandCompletionFunc( "toggle", Cvar_CompleteCvarName ); Cmd_AddCommand ("set", Cvar_Set_f); + Cmd_SetCommandCompletionFunc( "set", Cvar_CompleteCvarName ); Cmd_AddCommand ("sets", Cvar_Set_f); + Cmd_SetCommandCompletionFunc( "sets", Cvar_CompleteCvarName ); Cmd_AddCommand ("setu", Cvar_Set_f); + Cmd_SetCommandCompletionFunc( "setu", Cvar_CompleteCvarName ); Cmd_AddCommand ("seta", Cvar_Set_f); + Cmd_SetCommandCompletionFunc( "seta", Cvar_CompleteCvarName ); Cmd_AddCommand ("reset", Cvar_Reset_f); + Cmd_SetCommandCompletionFunc( "reset", Cvar_CompleteCvarName ); Cmd_AddCommand ("cvarlist", Cvar_List_f); Cmd_AddCommand ("cvar_restart", Cvar_Restart_f); } diff --git a/src/qcommon/msg.c b/src/qcommon/msg.c index 8be19aca..34434d15 100644 --- a/src/qcommon/msg.c +++ b/src/qcommon/msg.c @@ -292,13 +292,9 @@ void MSG_WriteLong( msg_t *sb, int c ) { } void MSG_WriteFloat( msg_t *sb, float f ) { - union { - float f; - int l; - } dat; - + floatint_t dat; dat.f = f; - MSG_WriteBits( sb, dat.l, 32 ); + MSG_WriteBits( sb, dat.i, 32 ); } void MSG_WriteString( msg_t *sb, const char *s ) { @@ -424,13 +420,9 @@ int MSG_ReadLong( msg_t *msg ) { } float MSG_ReadFloat( msg_t *msg ) { - union { - byte b[4]; - float f; - int l; - } dat; + floatint_t dat; - dat.l = MSG_ReadBits( msg, 32 ); + dat.i = MSG_ReadBits( msg, 32 ); if ( msg->readcount > msg->cursize ) { dat.f = -1; } @@ -552,20 +544,22 @@ int MSG_ReadDelta( msg_t *msg, int oldV, int bits ) { } void MSG_WriteDeltaFloat( msg_t *msg, float oldV, float newV ) { + floatint_t fi; if ( oldV == newV ) { MSG_WriteBits( msg, 0, 1 ); return; } + fi.f = newV; MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, *(int *)&newV, 32 ); + MSG_WriteBits( msg, fi.i, 32 ); } float MSG_ReadDeltaFloat( msg_t *msg, float oldV ) { if ( MSG_ReadBits( msg, 1 ) ) { - float newV; + floatint_t fi; - *(int *)&newV = MSG_ReadBits( msg, 32 ); - return newV; + fi.i = MSG_ReadBits( msg, 32 ); + return fi.f; } return oldV; } @@ -606,20 +600,22 @@ int MSG_ReadDeltaKey( msg_t *msg, int key, int oldV, int bits ) { } void MSG_WriteDeltaKeyFloat( msg_t *msg, int key, float oldV, float newV ) { + floatint_t fi; if ( oldV == newV ) { MSG_WriteBits( msg, 0, 1 ); return; } + fi.f = newV; MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, (*(int *)&newV) ^ key, 32 ); + MSG_WriteBits( msg, fi.i ^ key, 32 ); } float MSG_ReadDeltaKeyFloat( msg_t *msg, int key, float oldV ) { if ( MSG_ReadBits( msg, 1 ) ) { - float newV; + floatint_t fi; - *(int *)&newV = MSG_ReadBits( msg, 32 ) ^ key; - return newV; + fi.i = MSG_ReadBits( msg, 32 ) ^ key; + return fi.f; } return oldV; } diff --git a/src/qcommon/net_ip.c b/src/qcommon/net_ip.c index baf70a17..e7a52996 100644 --- a/src/qcommon/net_ip.c +++ b/src/qcommon/net_ip.c @@ -227,6 +227,7 @@ static void NetadrToSockadr( netadr_t *a, struct sockaddr *s ) { ((struct sockaddr_in6 *)s)->sin6_family = AF_INET6; ((struct sockaddr_in6 *)s)->sin6_addr = * ((struct in6_addr *) &a->ip6); ((struct sockaddr_in6 *)s)->sin6_port = a->port; + ((struct sockaddr_in6 *)s)->sin6_scope_id = a->scope_id; } else if(a->type == NA_MULTICAST6) { @@ -248,6 +249,7 @@ static void SockadrToNetadr( struct sockaddr *s, netadr_t *a ) { a->type = NA_IP6; memcpy(a->ip6, &((struct sockaddr_in6 *)s)->sin6_addr, sizeof(a->ip6)); a->port = ((struct sockaddr_in6 *)s)->sin6_port; + a->scope_id = ((struct sockaddr_in6 *)s)->sin6_scope_id; } } @@ -279,14 +281,11 @@ static qboolean Sys_StringToSockaddr(const char *s, struct sockaddr *sadr, int s memset(sadr, '\0', sizeof(*sadr)); memset(&hints, '\0', sizeof(hints)); - // workaround for buggy MacOSX getaddrinfo implementation that doesn't handle AF_UNSPEC in hints correctly. - if(family == AF_UNSPEC) - hintsp = NULL; - else - { - hintsp = &hints; - hintsp->ai_family = family; - } + hintsp = &hints; + hintsp->ai_family = family; + hintsp->ai_socktype = SOCK_DGRAM; + // FIXME: we should set "->ai_flags" to AI_PASSIVE if we intend + // to use this structure for a bind() - instead of a sendto() retval = getaddrinfo(s, NULL, hintsp, &res); @@ -340,8 +339,15 @@ static qboolean Sys_StringToSockaddr(const char *s, struct sockaddr *sadr, int s Sys_SockaddrToString ============= */ -static void Sys_SockaddrToString(char *dest, int destlen, struct sockaddr *input, int inputlen) +static void Sys_SockaddrToString(char *dest, int destlen, struct sockaddr *input) { + socklen_t inputlen; + + if (input->sa_family == AF_INET6) + inputlen = sizeof(struct sockaddr_in6); + else + inputlen = sizeof(struct sockaddr_in); + getnameinfo(input, inputlen, dest, destlen, NULL, 0, NI_NUMERICHOST); } @@ -399,7 +405,7 @@ qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) if (a.type == NA_IP6) { - if(!memcmp(a.ip6, b.ip6, sizeof(a.ip6))) + if(!memcmp(a.ip6, b.ip6, sizeof(a.ip6)) && a.scope_id == b.scope_id) return qtrue; return qfalse; @@ -423,7 +429,7 @@ const char *NET_AdrToString (netadr_t a) memset(&sadr, 0, sizeof(sadr)); NetadrToSockadr(&a, (struct sockaddr *) &sadr); - Sys_SockaddrToString(s, sizeof(s), (struct sockaddr *) &sadr, sizeof(sadr)); + Sys_SockaddrToString(s, sizeof(s), (struct sockaddr *) &sadr); } return s; @@ -718,6 +724,8 @@ qboolean Sys_IsLANAddress( netadr_t adr ) { } else { + // TODO? should we check the scope_id here? + compareip = (byte *) &((struct sockaddr_in6 *) &localIP[index].addr)->sin6_addr; comparemask = (byte *) &((struct sockaddr_in6 *) &localIP[index].netmask)->sin6_addr; compareadr = adr.ip6; @@ -755,7 +763,7 @@ void Sys_ShowIP(void) { for(i = 0; i < numIP; i++) { - Sys_SockaddrToString(addrbuf, sizeof(addrbuf), (struct sockaddr *) &localIP[i].addr, sizeof((*localIP).addr)); + Sys_SockaddrToString(addrbuf, sizeof(addrbuf), (struct sockaddr *) &localIP[i].addr); if(localIP[i].type == NA_IP) Com_Printf( "IP: %s\n", addrbuf); @@ -776,7 +784,7 @@ NET_IPSocket int NET_IPSocket( char *net_interface, int port, int *err ) { SOCKET newsocket; struct sockaddr_in address; - qboolean _true = qtrue; + u_long _true = 1; int i = 1; *err = 0; @@ -794,7 +802,7 @@ int NET_IPSocket( char *net_interface, int port, int *err ) { return newsocket; } // make it non-blocking - if( ioctlsocket( newsocket, FIONBIO, (u_long *)&_true ) == SOCKET_ERROR ) { + if( ioctlsocket( newsocket, FIONBIO, &_true ) == SOCKET_ERROR ) { Com_Printf( "WARNING: NET_IPSocket: ioctl FIONBIO: %s\n", NET_ErrorString() ); *err = socketError; closesocket(newsocket); @@ -847,7 +855,7 @@ NET_IP6Socket int NET_IP6Socket( char *net_interface, int port, struct sockaddr_in6 *bindto, int *err ) { SOCKET newsocket; struct sockaddr_in6 address; - qboolean _true = qtrue; + u_long _true = 1; *err = 0; @@ -869,7 +877,7 @@ int NET_IP6Socket( char *net_interface, int port, struct sockaddr_in6 *bindto, i } // make it non-blocking - if( ioctlsocket( newsocket, FIONBIO, (u_long *)&_true ) == SOCKET_ERROR ) { + if( ioctlsocket( newsocket, FIONBIO, &_true ) == SOCKET_ERROR ) { Com_Printf( "WARNING: NET_IP6Socket: ioctl FIONBIO: %s\n", NET_ErrorString() ); *err = socketError; closesocket(newsocket); diff --git a/src/qcommon/parse.c b/src/qcommon/parse.c index 2156f75b..c3b6e0ce 100644 --- a/src/qcommon/parse.c +++ b/src/qcommon/parse.c @@ -2546,7 +2546,7 @@ static int Parse_Directive_include(source_t *source) break; } if (token.type == TT_PUNCTUATION && *token.string == '>') break; - strncat(path, token.string, MAX_QPATH); + strncat(path, token.string, MAX_QPATH - 1); } if (*token.string != '>') { diff --git a/src/qcommon/q_math.c b/src/qcommon/q_math.c index a3a7b191..e7924073 100644 --- a/src/qcommon/q_math.c +++ b/src/qcommon/q_math.c @@ -550,10 +550,7 @@ void VectorRotate( vec3_t in, vec3_t matrix[3], vec3_t out ) */ float Q_rsqrt( float number ) { - union { - float f; - int i; - } t; + floatint_t t; float x2, y; const float threehalfs = 1.5F; @@ -568,9 +565,10 @@ float Q_rsqrt( float number ) } float Q_fabs( float f ) { - int tmp = * ( int * ) &f; - tmp &= 0x7FFFFFFF; - return * ( float * ) &tmp; + floatint_t fi; + fi.f = f; + fi.i &= 0x7FFFFFFF; + return fi.f; } #endif @@ -1587,15 +1585,11 @@ Don't pass doubles to this */ int Q_isnan( float x ) { - union - { - float f; - unsigned int i; - } t; + floatint_t fi; - t.f = x; - t.i &= 0x7FFFFFFF; - t.i = 0x7F800000 - t.i; + fi.f = x; + fi.ui &= 0x7FFFFFFF; + fi.ui = 0x7F800000 - fi.ui; - return (int)( (unsigned int)t.i >> 31 ); + return (int)( (unsigned int)fi.ui >> 31 ); } diff --git a/src/qcommon/q_shared.c b/src/qcommon/q_shared.c index 81248644..fc259219 100644 --- a/src/qcommon/q_shared.c +++ b/src/qcommon/q_shared.c @@ -206,16 +206,11 @@ qint64 Long64NoSwap (qint64 ll) return ll; } -typedef union { - float f; - unsigned int i; -} _FloatByteUnion; - float FloatSwap (const float *f) { - _FloatByteUnion out; + floatint_t out; out.f = *f; - out.i = LongSwap(out.i); + out.ui = LongSwap(out.ui); return out.f; } @@ -362,48 +357,48 @@ int COM_Compress( char *data_p ) { in++; if ( *in ) in += 2; - // record when we hit a newline - } else if ( c == '\n' || c == '\r' ) { - newline = qtrue; - in++; - // record when we hit whitespace - } else if ( c == ' ' || c == '\t') { - whitespace = qtrue; - in++; - // an actual token + // record when we hit a newline + } else if ( c == '\n' || c == '\r' ) { + newline = qtrue; + in++; + // record when we hit whitespace + } else if ( c == ' ' || c == '\t') { + whitespace = qtrue; + in++; + // an actual token } else { - // if we have a pending newline, emit it (and it counts as whitespace) - if (newline) { - *out++ = '\n'; - newline = qfalse; - whitespace = qfalse; - } if (whitespace) { - *out++ = ' '; - whitespace = qfalse; - } - - // copy quoted strings unmolested - if (c == '"') { - *out++ = c; - in++; - while (1) { - c = *in; - if (c && c != '"') { - *out++ = c; - in++; - } else { - break; - } - } - if (c == '"') { - *out++ = c; - in++; - } - } else { - *out = c; - out++; - in++; - } + // if we have a pending newline, emit it (and it counts as whitespace) + if (newline) { + *out++ = '\n'; + newline = qfalse; + whitespace = qfalse; + } if (whitespace) { + *out++ = ' '; + whitespace = qfalse; + } + + // copy quoted strings unmolested + if (c == '"') { + *out++ = c; + in++; + while (1) { + c = *in; + if (c && c != '"') { + *out++ = c; + in++; + } else { + break; + } + } + if (c == '"') { + *out++ = c; + in++; + } + } else { + *out = c; + out++; + in++; + } } } } @@ -513,62 +508,6 @@ char *COM_ParseExt( char **data_p, qboolean allowLineBreaks ) return com_token; } - -#if 0 -// no longer used -/* -=============== -COM_ParseInfos -=============== -*/ -int COM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] ) { - char *token; - int count; - char key[MAX_TOKEN_CHARS]; - - count = 0; - - while ( 1 ) { - token = COM_Parse( &buf ); - if ( !token[0] ) { - break; - } - if ( strcmp( token, "{" ) ) { - Com_Printf( "Missing { in info file\n" ); - break; - } - - if ( count == max ) { - Com_Printf( "Max infos exceeded\n" ); - break; - } - - infos[count][0] = 0; - while ( 1 ) { - token = COM_ParseExt( &buf, qtrue ); - if ( !token[0] ) { - Com_Printf( "Unexpected end of info file\n" ); - break; - } - if ( !strcmp( token, "}" ) ) { - break; - } - Q_strncpyz( key, token, sizeof( key ) ); - - token = COM_ParseExt( &buf, qfalse ); - if ( !token[0] ) { - strcpy( token, "" ); - } - Info_SetValueForKey( infos[count], key, token ); - } - count++; - } - - return count; -} -#endif - - /* ================== COM_MatchToken @@ -670,6 +609,44 @@ void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m) { COM_MatchToken( buf_p, ")" ); } +/* +=================== +Com_HexStrToInt +=================== +*/ +int Com_HexStrToInt( const char *str ) +{ + if ( !str || !str[ 0 ] ) + return -1; + + // check for hex code + if( str[ 0 ] == '0' && str[ 1 ] == 'x' ) + { + int i, n = 0; + + for( i = 2; i < strlen( str ); i++ ) + { + char digit; + + n *= 16; + + digit = tolower( str[ i ] ); + + if( digit >= '0' && digit <= '9' ) + digit -= '0'; + else if( digit >= 'a' && digit <= 'f' ) + digit = digit - 'a' + 10; + else + return -1; + + n += digit; + } + + return n; + } + + return -1; +} /* ============================================================================ @@ -734,11 +711,12 @@ qboolean Q_isanumber( const char *s ) return qfalse; #else char *p; + double d; if( *s == '\0' ) return qfalse; - strtod( s, &p ); + d = strtod( s, &p ); return *p == '\0'; #endif diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h index 6eb636d6..c24fe292 100644 --- a/src/qcommon/q_shared.h +++ b/src/qcommon/q_shared.h @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // q_shared.h -- included first by ALL program modules. // A user mod should never modify this file -#define PRODUCT_NAME "tremulous" +#define PRODUCT_NAME "tremulous" #ifdef _MSC_VER # define PRODUCT_VERSION "1.1.0" @@ -37,6 +37,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define CLIENT_WINDOW_MIN_TITLE "Tremulous" #define Q3_VERSION PRODUCT_NAME " " PRODUCT_VERSION +#define GAMENAME_FOR_MASTER "Tremulous" + #define MAX_TEAMNAME 32 #ifdef _MSC_VER @@ -142,6 +144,12 @@ typedef unsigned char byte; typedef enum {qfalse, qtrue} qboolean; +typedef union { + float f; + int i; + unsigned int ui; +} floatint_t; + typedef int qhandle_t; typedef int sfxHandle_t; typedef int fileHandle_t; @@ -704,6 +712,7 @@ void SkipRestOfLine ( char **data ); void Parse1DMatrix (char **buf_p, int x, float *m); void Parse2DMatrix (char **buf_p, int y, int x, float *m); void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m); +int Com_HexStrToInt( const char *str ); void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h index d6f7ea03..e4234f54 100644 --- a/src/qcommon/qcommon.h +++ b/src/qcommon/qcommon.h @@ -153,6 +153,7 @@ typedef struct { byte ip6[16]; unsigned short port; + unsigned long scope_id; // Needed for IPv6 link-local addresses } netadr_t; void NET_Init( void ); @@ -341,12 +342,9 @@ void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ); #define VMA(x) VM_ArgPtr(args[x]) static ID_INLINE float _vmf(intptr_t x) { - union { - intptr_t l; - float f; - } t; - t.l = x; - return t.f; + floatint_t fi; + fi.i = (int) x; + return fi.f; } #define VMF(x) _vmf(args[x]) @@ -406,8 +404,14 @@ void Cmd_AddCommand( const char *cmd_name, xcommand_t function ); void Cmd_RemoveCommand( const char *cmd_name ); +typedef void (*completionFunc_t)( char *args, int argNum ); + void Cmd_CommandCompletion( void(*callback)(const char *s) ); // callback with each valid string +void Cmd_SetCommandCompletionFunc( const char *command, + completionFunc_t complete ); +void Cmd_CompleteArgument( const char *command, char *args, int argNum ); +void Cmd_CompleteCfgName( char *args, int argNum ); int Cmd_Argc (void); char *Cmd_Argv (int arg); @@ -417,6 +421,7 @@ char *Cmd_ArgsFrom( int arg ); void Cmd_ArgsBuffer( char *buffer, int bufferLength ); void Cmd_LiteralArgsBuffer( char *buffer, int bufferLength ); char *Cmd_Cmd (void); +void Cmd_Args_Sanitize( void ); // The functions that execute commands get their parameters with these // functions. Cmd_Argv () will return an empty string, not a NULL // if arg > argc, so string operations are allways safe. @@ -521,6 +526,8 @@ void Cvar_CheckRange( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeI void Cvar_Restart_f( void ); +void Cvar_CompleteCvarName( char *args, int argNum ); + extern int cvar_modifiedFlags; // whenever a cvar is modifed, its flags will be OR'd into this, so // a single check can determine if any CVAR_USERINFO, CVAR_SERVERINFO, @@ -702,6 +709,11 @@ typedef struct { void Field_Clear( field_t *edit ); void Field_AutoComplete( field_t *edit ); +void Field_CompleteKeyname( void ); +void Field_CompleteFilename( const char *dir, + const char *ext, qboolean stripExt ); +void Field_CompleteCommand( char *cmd, + qboolean doCommands, qboolean doCvars ); /* ============================================================== diff --git a/src/qcommon/unzip.c b/src/qcommon/unzip.c index 90f5354d..7669aa95 100644 --- a/src/qcommon/unzip.c +++ b/src/qcommon/unzip.c @@ -1109,7 +1109,8 @@ static int unzlocal_getShort (FILE* fin, uLong *pX) { short v; - fread( &v, sizeof(v), 1, fin ); + size_t size; + size = fread( &v, sizeof(v), 1, fin ); *pX = LittleShort( v); return UNZ_OK; @@ -1138,7 +1139,8 @@ static int unzlocal_getLong (FILE *fin, uLong *pX) { int v; - fread( &v, sizeof(v), 1, fin ); + size_t size; + size = fread( &v, sizeof(v), 1, fin ); *pX = LittleLong( v); return UNZ_OK; diff --git a/src/qcommon/vm.c b/src/qcommon/vm.c index 7a38b8db..c1a829a6 100644 --- a/src/qcommon/vm.c +++ b/src/qcommon/vm.c @@ -219,7 +219,11 @@ VM_LoadSymbols */ void VM_LoadSymbols( vm_t *vm ) { int len; - char *mapfile, *text_p, *token; + union { + char *c; + void *v; + } mapfile; + char *text_p, *token; char name[MAX_QPATH]; char symbols[MAX_QPATH]; vmSymbol_t **prev, *sym; @@ -236,8 +240,8 @@ void VM_LoadSymbols( vm_t *vm ) { COM_StripExtension(vm->name, name, sizeof(name)); Com_sprintf( symbols, sizeof( symbols ), "vm/%s.map", name ); - len = FS_ReadFile( symbols, (void **)&mapfile ); - if ( !mapfile ) { + len = FS_ReadFile( symbols, &mapfile.v ); + if ( !mapfile.c ) { Com_Printf( "Couldn't load symbol file: %s\n", symbols ); return; } @@ -245,7 +249,7 @@ void VM_LoadSymbols( vm_t *vm ) { numInstructions = vm->instructionPointersLength >> 2; // parse the symbols - text_p = mapfile; + text_p = mapfile.c; prev = &vm->symbols; count = 0; @@ -292,7 +296,7 @@ void VM_LoadSymbols( vm_t *vm ) { vm->numSymbols = count; Com_Printf( "%i symbols parsed from %s\n", count, symbols ); - FS_FreeFile( mapfile ); + FS_FreeFile( mapfile.v ); } /* @@ -365,47 +369,50 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) { int dataLength; int i; char filename[MAX_QPATH]; - vmHeader_t *header; + union { + vmHeader_t *h; + void *v; + } header; // load the image Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name ); Com_Printf( "Loading vm file %s...\n", filename ); - length = FS_ReadFile( filename, (void **)&header ); - if ( !header ) { + length = FS_ReadFile( filename, &header.v ); + if ( !header.h ) { Com_Printf( "Failed.\n" ); VM_Free( vm ); return NULL; } - if( LittleLong( header->vmMagic ) == VM_MAGIC_VER2 ) { + if( LittleLong( header.h->vmMagic ) == VM_MAGIC_VER2 ) { Com_Printf( "...which has vmMagic VM_MAGIC_VER2\n" ); // byte swap the header for ( i = 0 ; i < sizeof( vmHeader_t ) / 4 ; i++ ) { - ((int *)header)[i] = LittleLong( ((int *)header)[i] ); + ((int *)header.h)[i] = LittleLong( ((int *)header.h)[i] ); } // validate - if ( header->jtrgLength < 0 - || header->bssLength < 0 - || header->dataLength < 0 - || header->litLength < 0 - || header->codeLength <= 0 ) { + if ( header.h->jtrgLength < 0 + || header.h->bssLength < 0 + || header.h->dataLength < 0 + || header.h->litLength < 0 + || header.h->codeLength <= 0 ) { VM_Free( vm ); Com_Error( ERR_FATAL, "%s has bad header", filename ); } - } else if( LittleLong( header->vmMagic ) == VM_MAGIC ) { + } else if( LittleLong( header.h->vmMagic ) == VM_MAGIC ) { // byte swap the header // sizeof( vmHeader_t ) - sizeof( int ) is the 1.32b vm header size for ( i = 0 ; i < ( sizeof( vmHeader_t ) - sizeof( int ) ) / 4 ; i++ ) { - ((int *)header)[i] = LittleLong( ((int *)header)[i] ); + ((int *)header.h)[i] = LittleLong( ((int *)header.h)[i] ); } // validate - if ( header->bssLength < 0 - || header->dataLength < 0 - || header->litLength < 0 - || header->codeLength <= 0 ) { + if ( header.h->bssLength < 0 + || header.h->dataLength < 0 + || header.h->litLength < 0 + || header.h->codeLength <= 0 ) { VM_Free( vm ); Com_Error( ERR_FATAL, "%s has bad header", filename ); } @@ -417,7 +424,7 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) { // round up to next power of 2 so all data operations can // be mask protected - dataLength = header->dataLength + header->litLength + header->bssLength; + dataLength = header.h->dataLength + header.h->litLength + header.h->bssLength; for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) { } dataLength = 1 << i; @@ -432,33 +439,34 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) { } // copy the intialized data - Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength ); + Com_Memcpy( vm->dataBase, (byte *)header.h + header.h->dataOffset, + header.h->dataLength + header.h->litLength ); // byte swap the longs - for ( i = 0 ; i < header->dataLength ; i += 4 ) { + for ( i = 0 ; i < header.h->dataLength ; i += 4 ) { *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) ); } - if( header->vmMagic == VM_MAGIC_VER2 ) { - vm->numJumpTableTargets = header->jtrgLength >> 2; + if( header.h->vmMagic == VM_MAGIC_VER2 ) { + vm->numJumpTableTargets = header.h->jtrgLength >> 2; Com_Printf( "Loading %d jump table targets\n", vm->numJumpTableTargets ); if( alloc ) { - vm->jumpTableTargets = Hunk_Alloc( header->jtrgLength, h_high ); + vm->jumpTableTargets = Hunk_Alloc( header.h->jtrgLength, h_high ); } else { - Com_Memset( vm->jumpTableTargets, 0, header->jtrgLength ); + Com_Memset( vm->jumpTableTargets, 0, header.h->jtrgLength ); } - Com_Memcpy( vm->jumpTableTargets, (byte *)header + header->dataOffset + - header->dataLength + header->litLength, header->jtrgLength ); + Com_Memcpy( vm->jumpTableTargets, (byte *)header.h + header.h->dataOffset + + header.h->dataLength + header.h->litLength, header.h->jtrgLength ); // byte swap the longs - for ( i = 0 ; i < header->jtrgLength ; i += 4 ) { + for ( i = 0 ; i < header.h->jtrgLength ; i += 4 ) { *(int *)(vm->jumpTableTargets + i) = LittleLong( *(int *)(vm->jumpTableTargets + i ) ); } } - return header; + return header.h; } /* diff --git a/src/qcommon/vm_powerpc.c b/src/qcommon/vm_powerpc.c new file mode 100644 index 00000000..76bb984a --- /dev/null +++ b/src/qcommon/vm_powerpc.c @@ -0,0 +1,2170 @@ +/* +=========================================================================== +Copyright (C) 2008 Przemyslaw Iskra + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include /* needed by sys/mman.h on OSX */ +#include +#include +#include +#include + +#ifndef MAP_ANONYMOUS +# define MAP_ANONYMOUS MAP_ANON +#endif + +#include "vm_local.h" +#include "vm_powerpc_asm.h" + +/* + * VM_TIMES enables showing information about time spent inside + * and outside generated code + */ +//#define VM_TIMES +#ifdef VM_TIMES +#include +static clock_t time_outside_vm = 0; +static clock_t time_total_vm = 0; +#endif + +/* exit() won't be called but use it because it is marked with noreturn */ +#define DIE( reason ) \ + do { \ + Com_Error(ERR_DROP, "vm_powerpc compiler error: " reason "\n"); \ + exit(1); \ + } while(0) + +/* + * vm_powerpc uses large quantities of memory during compilation, + * Z_Malloc memory may not be enough for some big qvm files + */ + +//#define VM_SYSTEM_MALLOC +#ifdef VM_SYSTEM_MALLOC +static inline void * +PPC_Malloc( size_t size ) +{ + void *mem = malloc( size ); + if ( ! mem ) + DIE( "Not enough memory" ); + + return mem; +} +# define PPC_Free free +#else +# define PPC_Malloc Z_Malloc +# define PPC_Free Z_Free +#endif + +/* + * optimizations: + * - hole: bubble optimization (OP_CONST+instruction) + * - copy: inline OP_BLOCK_COPY for lengths under 16/32 bytes + * - mask: use rlwinm instruction as dataMask + */ + +#ifdef __OPTIMIZE__ +# define OPTIMIZE_HOLE 1 +# define OPTIMIZE_COPY 1 +# define OPTIMIZE_MASK 1 +#else +# define OPTIMIZE_HOLE 0 +# define OPTIMIZE_COPY 0 +# define OPTIMIZE_MASK 0 +#endif + +/* + * SUPPORTED TARGETS: + * - Linux 32 bits + * ( http://refspecs.freestandards.org/elf/elfspec_ppc.pdf ) + * * LR at r0 + 4 + * * Local variable space not needed + * -> store caller safe regs at 16+ + * + * - Linux 64 bits (not fully conformant) + * ( http://www.ibm.com/developerworks/linux/library/l-powasm4.html ) + * * needs "official procedure descriptors" (only first function has one) + * * LR at r0 + 16 + * * local variable space required, min 64 bytes, starts at 48 + * -> store caller safe regs at 128+ + * + * - OS X 32 bits + * ( http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/Articles/32bitPowerPC.html ) + * * LR at r0 + 8 + * * local variable space required, min 32 bytes (?), starts at 24 + * -> store caller safe regs at 64+ + * + * - OS X 64 bits (completely untested) + * ( http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/Articles/64bitPowerPC.html ) + * * LR at r0 + 16 + * * local variable space required, min 64 bytes (?), starts at 48 + * -> store caller safe regs at 128+ + */ + +/* Select Length - first value on 32 bits, second on 64 */ +#ifdef __PPC64__ +# define SL( a, b ) (b) +#else +# define SL( a, b ) (a) +#endif + +/* Select ABI - first for ELF, second for OS X */ +#ifdef __ELF__ +# define SA( a, b ) (a) +#else +# define SA( a, b ) (b) +#endif + +#define ELF32 SL( SA( 1, 0 ), 0 ) +#define ELF64 SL( 0, SA( 1, 0 ) ) +#define OSX32 SL( SA( 0, 1 ), 0 ) +#define OSX64 SL( 0, SA( 0, 1 ) ) + +/* native length load/store instructions ( L stands for long ) */ +#define iSTLU SL( iSTWU, iSTDU ) +#define iSTL SL( iSTW, iSTD ) +#define iLL SL( iLWZ, iLD ) +#define iLLX SL( iLWZX, iLDX ) + +/* register length */ +#define GPRLEN SL( 4, 8 ) +#define FPRLEN (8) +/* shift that many bits to obtain value miltiplied by GPRLEN */ +#define GPRLEN_SHIFT SL( 2, 3 ) + +/* Link register position */ +#define STACK_LR SL( SA( 4, 8 ), 16 ) +/* register save position */ +#define STACK_SAVE SL( SA( 16, 64 ), 128 ) +/* temporary space, for float<->int exchange */ +#define STACK_TEMP SL( SA( 8, 24 ), 48 ) +/* red zone temporary space, used instead of STACK_TEMP if stack isn't + * prepared properly */ +#define STACK_RTEMP (-16) + +#if ELF64 +/* + * Official Procedure Descriptor + * we need to prepare one for generated code if we want to call it + * as function + */ +typedef struct { + void *function; + void *toc; + void *env; +} opd_t; +#endif + + +/* + * opcode information table: + * - length of immediate value + * - returned register type + * - required register(s) type + */ +#define opImm0 0x0000 /* no immediate */ +#define opImm1 0x0001 /* 1 byte immadiate value after opcode */ +#define opImm4 0x0002 /* 4 bytes immediate value after opcode */ + +#define opRet0 0x0000 /* returns nothing */ +#define opRetI 0x0004 /* returns integer */ +#define opRetF 0x0008 /* returns float */ +#define opRetIF (opRetI | opRetF) /* returns integer or float */ + +#define opArg0 0x0000 /* requires nothing */ +#define opArgI 0x0010 /* requires integer(s) */ +#define opArgF 0x0020 /* requires float(s) */ +#define opArgIF (opArgI | opArgF) /* requires integer or float */ + +#define opArg2I 0x0040 /* requires second argument, integer */ +#define opArg2F 0x0080 /* requires second argument, float */ +#define opArg2IF (opArg2I | opArg2F) /* requires second argument, integer or float */ + +static const unsigned char vm_opInfo[256] = +{ + [OP_UNDEF] = opImm0, + [OP_IGNORE] = opImm0, + [OP_BREAK] = opImm0, + [OP_ENTER] = opImm4, + /* OP_LEAVE has to accept floats, they will be converted to ints */ + [OP_LEAVE] = opImm4 | opRet0 | opArgIF, + /* only STORE4 and POP use values from OP_CALL, + * no need to convert floats back */ + [OP_CALL] = opImm0 | opRetI | opArgI, + [OP_PUSH] = opImm0 | opRetIF, + [OP_POP] = opImm0 | opRet0 | opArgIF, + [OP_CONST] = opImm4 | opRetIF, + [OP_LOCAL] = opImm4 | opRetI, + [OP_JUMP] = opImm0 | opRet0 | opArgI, + + [OP_EQ] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_NE] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_LTI] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_LEI] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_GTI] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_GEI] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_LTU] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_LEU] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_GTU] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_GEU] = opImm4 | opRet0 | opArgI | opArg2I, + [OP_EQF] = opImm4 | opRet0 | opArgF | opArg2F, + [OP_NEF] = opImm4 | opRet0 | opArgF | opArg2F, + [OP_LTF] = opImm4 | opRet0 | opArgF | opArg2F, + [OP_LEF] = opImm4 | opRet0 | opArgF | opArg2F, + [OP_GTF] = opImm4 | opRet0 | opArgF | opArg2F, + [OP_GEF] = opImm4 | opRet0 | opArgF | opArg2F, + + [OP_LOAD1] = opImm0 | opRetI | opArgI, + [OP_LOAD2] = opImm0 | opRetI | opArgI, + [OP_LOAD4] = opImm0 | opRetIF| opArgI, + [OP_STORE1] = opImm0 | opRet0 | opArgI | opArg2I, + [OP_STORE2] = opImm0 | opRet0 | opArgI | opArg2I, + [OP_STORE4] = opImm0 | opRet0 | opArgIF| opArg2I, + [OP_ARG] = opImm1 | opRet0 | opArgIF, + [OP_BLOCK_COPY] = opImm4 | opRet0 | opArgI | opArg2I, + + [OP_SEX8] = opImm0 | opRetI | opArgI, + [OP_SEX16] = opImm0 | opRetI | opArgI, + [OP_NEGI] = opImm0 | opRetI | opArgI, + [OP_ADD] = opImm0 | opRetI | opArgI | opArg2I, + [OP_SUB] = opImm0 | opRetI | opArgI | opArg2I, + [OP_DIVI] = opImm0 | opRetI | opArgI | opArg2I, + [OP_DIVU] = opImm0 | opRetI | opArgI | opArg2I, + [OP_MODI] = opImm0 | opRetI | opArgI | opArg2I, + [OP_MODU] = opImm0 | opRetI | opArgI | opArg2I, + [OP_MULI] = opImm0 | opRetI | opArgI | opArg2I, + [OP_MULU] = opImm0 | opRetI | opArgI | opArg2I, + [OP_BAND] = opImm0 | opRetI | opArgI | opArg2I, + [OP_BOR] = opImm0 | opRetI | opArgI | opArg2I, + [OP_BXOR] = opImm0 | opRetI | opArgI | opArg2I, + [OP_BCOM] = opImm0 | opRetI | opArgI, + [OP_LSH] = opImm0 | opRetI | opArgI | opArg2I, + [OP_RSHI] = opImm0 | opRetI | opArgI | opArg2I, + [OP_RSHU] = opImm0 | opRetI | opArgI | opArg2I, + [OP_NEGF] = opImm0 | opRetF | opArgF, + [OP_ADDF] = opImm0 | opRetF | opArgF | opArg2F, + [OP_SUBF] = opImm0 | opRetF | opArgF | opArg2F, + [OP_DIVF] = opImm0 | opRetF | opArgF | opArg2F, + [OP_MULF] = opImm0 | opRetF | opArgF | opArg2F, + [OP_CVIF] = opImm0 | opRetF | opArgI, + [OP_CVFI] = opImm0 | opRetI | opArgF, +}; + +/* + * source instruction data + */ +typedef struct source_instruction_s source_instruction_t; +struct source_instruction_s { + // opcode + unsigned long int op; + + // number of instruction + unsigned long int i_count; + + // immediate value (if any) + union { + unsigned int i; + signed int si; + signed short ss[2]; + unsigned short us[2]; + unsigned char b; + } arg; + + // required and returned registers + unsigned char regA1; + unsigned char regA2; + unsigned char regR; + unsigned char regPos; + + // next instruction + source_instruction_t *next; +}; + + + +/* + * read-only data needed by the generated code + */ +typedef struct VM_Data { + // length of this struct + data + size_t dataLength; + // compiled code size (in bytes) + // it only is code size, without the data + size_t codeLength; + + // function pointers, no use to waste registers for them + long int (* AsmCall)( int, int ); + void (* BlockCopy )( unsigned int, unsigned int, unsigned int ); + + // instruction pointers, rarely used so don't waste register + ppc_instruction_t *iPointers; + + // data mask for load and store, not used if optimized + unsigned int dataMask; + + // fixed number used to convert from integer to float + unsigned int floatBase; // 0x59800004 + +#if ELF64 + // official procedure descriptor + opd_t opd; +#endif + + // additional constants, for floating point OP_CONST + // this data has dynamic length, thus '0' here + unsigned int data[0]; +} vm_data_t; + +#ifdef offsetof +# define VM_Data_Offset( field ) offsetof( vm_data_t, field ) +#else +# define OFFSET( structName, field ) \ + ( (void *)&(((structName *)NULL)->field) - NULL ) +# define VM_Data_Offset( field ) OFFSET( vm_data_t, field ) +#endif + + +/* + * functions used by generated code + */ +static long int +VM_AsmCall( int callSyscallInvNum, int callProgramStack ) +{ + vm_t *savedVM = currentVM; + long int i, ret; +#ifdef VM_TIMES + struct tms start_time, stop_time; + clock_t saved_time = time_outside_vm; + times( &start_time ); +#endif + + // save the stack to allow recursive VM entry + currentVM->programStack = callProgramStack - 4; + + // we need to convert ints to longs on 64bit powerpcs + if ( sizeof( intptr_t ) == sizeof( int ) ) { + intptr_t *argPosition = (intptr_t *)((byte *)currentVM->dataBase + callProgramStack + 4); + + // generated code does not invert syscall number + argPosition[ 0 ] = -1 - callSyscallInvNum; + + ret = currentVM->systemCall( argPosition ); + } else { + intptr_t args[11]; + + // generated code does not invert syscall number + args[0] = -1 - callSyscallInvNum; + + int *argPosition = (int *)((byte *)currentVM->dataBase + callProgramStack + 4); + for( i = 1; i < 11; i++ ) + args[ i ] = argPosition[ i ]; + + ret = currentVM->systemCall( args ); + } + + currentVM = savedVM; + +#ifdef VM_TIMES + times( &stop_time ); + time_outside_vm = saved_time + ( stop_time.tms_utime - start_time.tms_utime ); +#endif + + return ret; +} + +static void +VM_BlockCopy( unsigned int dest, unsigned int src, unsigned int count ) +{ + unsigned dataMask = currentVM->dataMask; + + if ( (dest & dataMask) != dest + || (src & dataMask) != src + || ((dest+count) & dataMask) != dest + count + || ((src+count) & dataMask) != src + count) + { + DIE( "OP_BLOCK_COPY out of range!"); + } + + memcpy( currentVM->dataBase+dest, currentVM->dataBase+src, count ); +} + + +/* + * code-block descriptors + */ +typedef struct dest_instruction dest_instruction_t; +typedef struct symbolic_jump symbolic_jump_t; + +struct symbolic_jump { + // number of source instruction it has to jump to + unsigned long int jump_to; + + // jump condition true/false, (4*cr7+(eq|gt..)) + long int bo, bi; + + // extensions / modifiers (branch-link) + unsigned long ext; + + // dest_instruction refering to this jump + dest_instruction_t *parent; + + // next jump + symbolic_jump_t *nextJump; +}; + +struct dest_instruction { + // position in the output chain + unsigned long int count; + + // source instruction number + unsigned long int i_count; + + // exact (for instructins), or maximum (for jump) length + unsigned short length; + + dest_instruction_t *next; + + // if the instruction is a jump than jump will be non NULL + symbolic_jump_t *jump; + + // if jump is NULL than all the instructions will be here + ppc_instruction_t code[0]; +}; + +// first and last instruction, +// di_first is a dummy instruction +static dest_instruction_t *di_first = NULL, *di_last = NULL; +// number of instructions +static unsigned long int di_count = 0; +// pointers needed to compute local jumps, those aren't pointers to +// actual instructions, just used to check how long the jump is going +// to be and whether it is positive or negative +static dest_instruction_t **di_pointers = NULL; + +// output instructions which does not come from source code +// use false i_count value +#define FALSE_ICOUNT 0xffffffff + + +/* + * append specified instructions at the end of instruction chain + */ +static void +PPC_Append( + dest_instruction_t *di_now, + unsigned long int i_count + ) +{ + di_now->count = di_count++; + di_now->i_count = i_count; + di_now->next = NULL; + + di_last->next = di_now; + di_last = di_now; + + if ( i_count != FALSE_ICOUNT ) { + if ( ! di_pointers[ i_count ] ) + di_pointers[ i_count ] = di_now; + } +} + +/* + * make space for instructions and append + */ +static void +PPC_AppendInstructions( + unsigned long int i_count, + size_t num_instructions, + const ppc_instruction_t *is + ) +{ + if ( num_instructions < 0 ) + num_instructions = 0; + size_t iBytes = sizeof( ppc_instruction_t ) * num_instructions; + dest_instruction_t *di_now = PPC_Malloc( sizeof( dest_instruction_t ) + iBytes ); + + di_now->length = num_instructions; + di_now->jump = NULL; + + if ( iBytes > 0 ) + memcpy( &(di_now->code[0]), is, iBytes ); + + PPC_Append( di_now, i_count ); +} + +/* + * create symbolic jump and append + */ +static symbolic_jump_t *sj_first = NULL, *sj_last = NULL; +static void +PPC_PrepareJump( + unsigned long int i_count, + unsigned long int dest, + long int bo, + long int bi, + unsigned long int ext + ) +{ + dest_instruction_t *di_now = PPC_Malloc( sizeof( dest_instruction_t ) ); + symbolic_jump_t *sj = PPC_Malloc( sizeof( symbolic_jump_t ) ); + + sj->jump_to = dest; + sj->bo = bo; + sj->bi = bi; + sj->ext = ext; + sj->parent = di_now; + sj->nextJump = NULL; + + sj_last->nextJump = sj; + sj_last = sj; + + di_now->length = (bo == branchAlways ? 1 : 2); + di_now->jump = sj; + + PPC_Append( di_now, i_count ); +} + +/* + * simplyfy instruction emission + */ +#define emitStart( i_cnt ) \ + unsigned long int i_count = i_cnt; \ + size_t num_instructions = 0; \ + long int force_emit = 0; \ + ppc_instruction_t instructions[50]; + +#define pushIn( inst ) \ + (instructions[ num_instructions++ ] = inst) +#define in( inst, args... ) pushIn( IN( inst, args ) ) + +#define emitEnd() \ + do{ \ + if ( num_instructions || force_emit ) \ + PPC_AppendInstructions( i_count, num_instructions, instructions );\ + num_instructions = 0; \ + } while(0) + +#define emitJump( dest, bo, bi, ext ) \ + do { \ + emitEnd(); \ + PPC_PrepareJump( i_count, dest, bo, bi, ext ); \ + } while(0) + + +/* + * definitions for creating .data section, + * used in cases where constant float is needed + */ +#define LOCAL_DATA_CHUNK 50 +typedef struct local_data_s local_data_t; +struct local_data_s { + // number of data in this structure + long int count; + + // data placeholder + unsigned int data[ LOCAL_DATA_CHUNK ]; + + // next chunk, if this one wasn't enough + local_data_t *next; +}; + +// first data chunk +static local_data_t *data_first = NULL; +// total number of data +static long int data_acc = 0; + +/* + * append the data and return its offset + */ +static size_t +PPC_PushData( unsigned int datum ) +{ + local_data_t *d_now = data_first; + long int accumulated = 0; + + // check whether we have this one already + do { + long int i; + for ( i = 0; i < d_now->count; i++ ) { + if ( d_now->data[ i ] == datum ) { + accumulated += i; + return VM_Data_Offset( data[ accumulated ] ); + } + } + if ( !d_now->next ) + break; + + accumulated += d_now->count; + d_now = d_now->next; + } while (1); + + // not found, need to append + accumulated += d_now->count; + + // last chunk is full, create new one + if ( d_now->count >= LOCAL_DATA_CHUNK ) { + d_now->next = PPC_Malloc( sizeof( local_data_t ) ); + d_now = d_now->next; + d_now->count = 0; + d_now->next = NULL; + } + + d_now->data[ d_now->count ] = datum; + d_now->count += 1; + + data_acc = accumulated + 1; + + return VM_Data_Offset( data[ accumulated ] ); +} + +/* + * find leading zeros in dataMask to implement it with + * "rotate and mask" instruction + */ +static long int fastMaskHi = 0, fastMaskLo = 31; +static void +PPC_MakeFastMask( int mask ) +{ +#if defined( __GNUC__ ) && ( __GNUC__ >= 4 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 4 ) ) + /* count leading zeros */ + fastMaskHi = __builtin_clz( mask ); + + /* count trailing zeros */ + fastMaskLo = 31 - __builtin_ctz( mask ); +#else + fastMaskHi = 0; + while ( ( mask & ( 0x80000000 >> fastMaskHi ) ) == 0 ) + fastMaskHi++; + + fastMaskLo = 31; + while ( ( mask & ( 0x80000000 >> fastMaskLo ) ) == 0 ) + fastMaskLo--; +#endif +} + + +/* + * register definitions + */ + +/* registers which are global for generated code */ + +// pointer to VM_Data (constant) +#define rVMDATA r14 +// vm->dataBase (constant) +#define rDATABASE r15 +// programStack (variable) +#define rPSTACK r16 + +/* + * function local registers, + * + * normally only volatile registers are used, but if there aren't enough + * or function has to preserve some value while calling annother one + * then caller safe registers are used as well + */ +static const long int gpr_list[] = { + /* caller safe registers, normally only one is used */ + r24, r23, r22, r21, + r20, r19, r18, r17, + /* volatile registers (preferred), + * normally no more than 5 is used */ + r3, r4, r5, r6, + r7, r8, r9, r10, +}; +static const long int gpr_vstart = 8; /* position of first volatile register */ +static const long int gpr_total = sizeof( gpr_list ) / sizeof( gpr_list[0] ); + +static const long int fpr_list[] = { + /* static registers, normally none is used */ + f20, f21, f19, f18, + f17, f16, f15, f14, + /* volatile registers (preferred), + * normally no more than 7 is used */ + f0, f1, f2, f3, + f4, f5, f6, f7, + f8, f9, f10, f11, + f12, f13, +}; +static const long int fpr_vstart = 8; +static const long int fpr_total = sizeof( fpr_list ) / sizeof( fpr_list[0] ); + +/* + * prepare some dummy structures and emit init code + */ +static void +PPC_CompileInit( void ) +{ + di_first = di_last = PPC_Malloc( sizeof( dest_instruction_t ) ); + di_first->count = 0; + di_first->next = NULL; + di_first->jump = NULL; + + sj_first = sj_last = PPC_Malloc( sizeof( symbolic_jump_t ) ); + sj_first->nextJump = NULL; + + data_first = PPC_Malloc( sizeof( local_data_t ) ); + data_first->count = 0; + data_first->next = NULL; + + /* + * init function: + * saves old values of global registers and sets our values + * function prototype is: + * int begin( void *data, int programStack, void *vm->dataBase ) + */ + + /* first instruction must not be placed on instruction list */ + emitStart( FALSE_ICOUNT ); + + long int stack = STACK_SAVE + 4 * GPRLEN; + + in( iMFLR, r0 ); + in( iSTLU, r1, -stack, r1 ); + in( iSTL, rVMDATA, STACK_SAVE + 0 * GPRLEN, r1 ); + in( iSTL, rPSTACK, STACK_SAVE + 1 * GPRLEN, r1 ); + in( iSTL, rDATABASE, STACK_SAVE + 2 * GPRLEN, r1 ); + in( iSTL, r0, stack + STACK_LR, r1 ); + in( iMR, rVMDATA, r3 ); + in( iMR, rPSTACK, r4 ); + in( iMR, rDATABASE, r5 ); + in( iBL, +4*8 ); // LINK JUMP: first generated instruction | XXX jump ! + in( iLL, rVMDATA, STACK_SAVE + 0 * GPRLEN, r1 ); + in( iLL, rPSTACK, STACK_SAVE + 1 * GPRLEN, r1 ); + in( iLL, rDATABASE, STACK_SAVE + 2 * GPRLEN, r1 ); + in( iLL, r0, stack + STACK_LR, r1 ); + in( iMTLR, r0 ); + in( iADDI, r1, r1, stack ); + in( iBLR ); + + emitEnd(); +} + +// rFIRST is the copy of the top value on the opstack +#define rFIRST (gpr_list[ gpr_pos - 1]) +// second value on the opstack +#define rSECOND (gpr_list[ gpr_pos - 2 ]) +// temporary registers, not on the opstack +#define rTEMP(x) (gpr_list[ gpr_pos + x ]) +#define rTMP rTEMP(0) + +#define fFIRST (fpr_list[ fpr_pos - 1 ]) +#define fSECOND (fpr_list[ fpr_pos - 2 ]) +#define fTEMP(x) (fpr_list[ fpr_pos + x ]) +#define fTMP fTEMP(0) + +// register types +#define rTYPE_STATIC 0x01 +#define rTYPE_FLOAT 0x02 + +// what type should this opcode return +#define RET_INT ( !(i_now->regR & rTYPE_FLOAT) ) +#define RET_FLOAT ( i_now->regR & rTYPE_FLOAT ) +// what type should it accept +#define ARG_INT ( ! i_now->regA1 ) +#define ARG_FLOAT ( i_now->regA1 ) +#define ARG2_INT ( ! i_now->regA2 ) +#define ARG2_FLOAT ( i_now->regA2 ) + +/* + * emit OP_CONST, called if nothing has used the const value directly + */ +static void +PPC_EmitConst( source_instruction_t * const i_const ) +{ + emitStart( i_const->i_count ); + + if ( !(i_const->regR & rTYPE_FLOAT) ) { + // gpr_pos needed for "rFIRST" to work + long int gpr_pos = i_const->regPos; + + if ( i_const->arg.si >= -0x8000 && i_const->arg.si < 0x8000 ) { + in( iLI, rFIRST, i_const->arg.si ); + } else if ( i_const->arg.i < 0x10000 ) { + in( iLI, rFIRST, 0 ); + in( iORI, rFIRST, rFIRST, i_const->arg.i ); + } else { + in( iLIS, rFIRST, i_const->arg.ss[ 0 ] ); + if ( i_const->arg.us[ 1 ] != 0 ) + in( iORI, rFIRST, rFIRST, i_const->arg.us[ 1 ] ); + } + + } else { + // fpr_pos needed for "fFIRST" to work + long int fpr_pos = i_const->regPos; + + // there's no good way to generate the data, + // just read it from data section + in( iLFS, fFIRST, PPC_PushData( i_const->arg.i ), rVMDATA ); + } + + emitEnd(); +} +#define MAYBE_EMIT_CONST() if ( i_const ) PPC_EmitConst( i_const ) + +/* + * emit empty instruction, just sets the needed pointers + */ +static inline void +PPC_EmitNull( source_instruction_t * const i_null ) +{ + PPC_AppendInstructions( i_null->i_count, 0, NULL ); +} +#define EMIT_FALSE_CONST() PPC_EmitNull( i_const ) + + +/* + * analize function for register usage and whether it needs stack (r1) prepared + */ +static void +VM_AnalyzeFunction( + source_instruction_t * const i_first, + long int *prepareStack, + long int *gpr_start_pos, + long int *fpr_start_pos + ) +{ + source_instruction_t *i_now = i_first; + + source_instruction_t *value_provider[20] = { NULL }; + unsigned long int opstack_depth = 0; + + /* + * first step: + * remember what codes returned some value and mark the value type + * when we get to know what it should be + */ + while ( (i_now = i_now->next) ) { + unsigned long int op = i_now->op; + unsigned long int opi = vm_opInfo[ op ]; + + if ( opi & opArgIF ) { + assert( opstack_depth > 0 ); + + opstack_depth--; + source_instruction_t *vp = value_provider[ opstack_depth ]; + unsigned long int vpopi = vm_opInfo[ vp->op ]; + + if ( (opi & opArgI) && (vpopi & opRetI) ) { + // instruction accepts integer, provider returns integer + //vp->regR |= rTYPE_INT; + //i_now->regA1 = rTYPE_INT; + } else if ( (opi & opArgF) && (vpopi & opRetF) ) { + // instruction accepts float, provider returns float + vp->regR |= rTYPE_FLOAT; // use OR here - could be marked as static + i_now->regA1 = rTYPE_FLOAT; + } else { + // instruction arg type does not agree with + // provider return type + DIE( "unrecognized instruction combination" ); + } + + } + if ( opi & opArg2IF ) { + assert( opstack_depth > 0 ); + + opstack_depth--; + source_instruction_t *vp = value_provider[ opstack_depth ]; + unsigned long int vpopi = vm_opInfo[ vp->op ]; + + if ( (opi & opArg2I) && (vpopi & opRetI) ) { + // instruction accepts integer, provider returns integer + //vp->regR |= rTYPE_INT; + //i_now->regA2 = rTYPE_INT; + } else if ( (opi & opArg2F) && (vpopi & opRetF) ) { + // instruction accepts float, provider returns float + vp->regR |= rTYPE_FLOAT; // use OR here - could be marked as static + i_now->regA2 = rTYPE_FLOAT; + } else { + // instruction arg type does not agree with + // provider return type + DIE( "unrecognized instruction combination" ); + } + } + + + if ( + ( op == OP_CALL ) + || + ( op == OP_BLOCK_COPY && ( i_now->arg.i > SL( 16, 32 ) || !OPTIMIZE_COPY ) ) + ) { + long int i; + *prepareStack = 1; + // force caller safe registers so we won't have to save them + for ( i = 0; i < opstack_depth; i++ ) { + source_instruction_t *vp = value_provider[ i ]; + vp->regR |= rTYPE_STATIC; + } + } + + + if ( opi & opRetIF ) { + value_provider[ opstack_depth ] = i_now; + opstack_depth++; + } + } + + /* + * second step: + * now that we know register types; compute exactly how many registers + * of each type we need + */ + + i_now = i_first; + long int needed_reg[4] = {0,0,0,0}, max_reg[4] = {0,0,0,0}; + opstack_depth = 0; + while ( (i_now = i_now->next) ) { + unsigned long int op = i_now->op; + unsigned long int opi = vm_opInfo[ op ]; + + if ( opi & opArgIF ) { + assert( opstack_depth > 0 ); + opstack_depth--; + source_instruction_t *vp = value_provider[ opstack_depth ]; + + needed_reg[ ( vp->regR & 2 ) ] -= 1; + if ( vp->regR & 1 ) // static + needed_reg[ ( vp->regR & 3 ) ] -= 1; + } + if ( opi & opArg2IF ) { + assert( opstack_depth > 0 ); + opstack_depth--; + source_instruction_t *vp = value_provider[ opstack_depth ]; + + needed_reg[ ( vp->regR & 2 ) ] -= 1; + if ( vp->regR & 1 ) // static + needed_reg[ ( vp->regR & 3 ) ] -= 1; + } + + if ( opi & opRetIF ) { + long int i; + value_provider[ opstack_depth ] = i_now; + opstack_depth++; + + i = i_now->regR & 2; + needed_reg[ i ] += 1; + if ( max_reg[ i ] < needed_reg[ i ] ) + max_reg[ i ] = needed_reg[ i ]; + + i = i_now->regR & 3; + if ( i & 1 ) { + needed_reg[ i ] += 1; + if ( max_reg[ i ] < needed_reg[ i ] ) + max_reg[ i ] = needed_reg[ i ]; + } + } + } + + long int gpr_start = gpr_vstart; + const long int gpr_volatile = gpr_total - gpr_vstart; + if ( max_reg[ 1 ] > 0 || max_reg[ 0 ] > gpr_volatile ) { + // max_reg[ 0 ] - all gprs needed + // max_reg[ 1 ] - static gprs needed + long int max = max_reg[ 0 ] - gpr_volatile; + if ( max_reg[ 1 ] > max ) + max = max_reg[ 1 ]; + if ( max > gpr_vstart ) { + /* error */ + DIE( "Need more GPRs" ); + } + + gpr_start -= max; + + // need stack to save caller safe registers + *prepareStack = 1; + } + *gpr_start_pos = gpr_start; + + long int fpr_start = fpr_vstart; + const long int fpr_volatile = fpr_total - fpr_vstart; + if ( max_reg[ 3 ] > 0 || max_reg[ 2 ] > fpr_volatile ) { + // max_reg[ 2 ] - all fprs needed + // max_reg[ 3 ] - static fprs needed + long int max = max_reg[ 2 ] - fpr_volatile; + if ( max_reg[ 3 ] > max ) + max = max_reg[ 3 ]; + if ( max > fpr_vstart ) { + /* error */ + DIE( "Need more FPRs" ); + } + + fpr_start -= max; + + // need stack to save caller safe registers + *prepareStack = 1; + } + *fpr_start_pos = fpr_start; +} + +/* + * translate opcodes to ppc instructions, + * it works on functions, not on whole code at once + */ +static void +VM_CompileFunction( source_instruction_t * const i_first ) +{ + long int prepareStack = 0; + long int gpr_start_pos, fpr_start_pos; + + VM_AnalyzeFunction( i_first, &prepareStack, &gpr_start_pos, &fpr_start_pos ); + + long int gpr_pos = gpr_start_pos, fpr_pos = fpr_start_pos; + + // OP_CONST combines well with many opcodes so we treat it in a special way + source_instruction_t *i_const = NULL; + source_instruction_t *i_now = i_first; + + // how big the stack has to be + long int save_space = STACK_SAVE; + { + if ( gpr_start_pos < gpr_vstart ) + save_space += (gpr_vstart - gpr_start_pos) * GPRLEN; + save_space = ( save_space + 15 ) & ~0x0f; + + if ( fpr_start_pos < fpr_vstart ) + save_space += (fpr_vstart - fpr_start_pos) * FPRLEN; + save_space = ( save_space + 15 ) & ~0x0f; + } + + long int stack_temp = prepareStack ? STACK_TEMP : STACK_RTEMP; + + while ( (i_now = i_now->next) ) { + emitStart( i_now->i_count ); + + switch ( i_now->op ) + { + default: + case OP_UNDEF: + case OP_IGNORE: + MAYBE_EMIT_CONST(); + in( iNOP ); + break; + + case OP_BREAK: + MAYBE_EMIT_CONST(); + // force SEGV + in( iLWZ, r0, 0, r0 ); + break; + + case OP_ENTER: + if ( i_const ) + DIE( "Weird opcode order" ); + + // don't prepare stack if not needed + if ( prepareStack ) { + long int i, save_pos = STACK_SAVE; + + in( iMFLR, r0 ); + in( iSTLU, r1, -save_space, r1 ); + in( iSTL, r0, save_space + STACK_LR, r1 ); + + /* save registers */ + for ( i = gpr_start_pos; i < gpr_vstart; i++ ) { + in( iSTL, gpr_list[ i ], save_pos, r1 ); + save_pos += GPRLEN; + } + save_pos = ( save_pos + 15 ) & ~0x0f; + + for ( i = fpr_start_pos; i < fpr_vstart; i++ ) { + in( iSTFD, fpr_list[ i ], save_pos, r1 ); + save_pos += FPRLEN; + } + prepareStack = 2; + } + + in( iADDI, rPSTACK, rPSTACK, - i_now->arg.si ); + break; + + case OP_LEAVE: + if ( i_const ) { + EMIT_FALSE_CONST(); + + if ( i_const->regR & rTYPE_FLOAT) + DIE( "constant float in OP_LEAVE" ); + + if ( i_const->arg.si >= -0x8000 && i_const->arg.si < 0x8000 ) { + in( iLI, r3, i_const->arg.si ); + } else if ( i_const->arg.i < 0x10000 ) { + in( iLI, r3, 0 ); + in( iORI, r3, r3, i_const->arg.i ); + } else { + in( iLIS, r3, i_const->arg.ss[ 0 ] ); + if ( i_const->arg.us[ 1 ] != 0 ) + in( iORI, r3, r3, i_const->arg.us[ 1 ] ); + } + gpr_pos--; + } else { + MAYBE_EMIT_CONST(); + + /* place return value in r3 */ + if ( ARG_INT ) { + if ( rFIRST != r3 ) + in( iMR, r3, rFIRST ); + gpr_pos--; + } else { + in( iSTFS, fFIRST, stack_temp, r1 ); + in( iLWZ, r3, stack_temp, r1 ); + fpr_pos--; + } + } + + // don't undo stack if not prepared + if ( prepareStack >= 2 ) { + long int i, save_pos = STACK_SAVE; + + in( iLL, r0, save_space + STACK_LR, r1 ); + + + /* restore registers */ + for ( i = gpr_start_pos; i < gpr_vstart; i++ ) { + in( iLL, gpr_list[ i ], save_pos, r1 ); + save_pos += GPRLEN; + } + save_pos = ( save_pos + 15 ) & ~0x0f; + for ( i = fpr_start_pos; i < fpr_vstart; i++ ) { + in( iLFD, fpr_list[ i ], save_pos, r1 ); + save_pos += FPRLEN; + } + + in( iMTLR, r0 ); + in( iADDI, r1, r1, save_space ); + } + in( iADDI, rPSTACK, rPSTACK, i_now->arg.si); + in( iBLR ); + assert( gpr_pos == gpr_start_pos ); + assert( fpr_pos == fpr_start_pos ); + break; + + case OP_CALL: + if ( i_const ) { + EMIT_FALSE_CONST(); + + if ( i_const->arg.si >= 0 ) { + emitJump( + i_const->arg.i, + branchAlways, 0, branchExtLink + ); + } else { + /* syscall */ + in( iLL, r0, VM_Data_Offset( AsmCall ), rVMDATA ); + + in( iLI, r3, i_const->arg.si ); // negative value + in( iMR, r4, rPSTACK ); // push PSTACK on argument list + + in( iMTCTR, r0 ); + in( iBCTRL ); + } + if ( rFIRST != r3 ) + in( iMR, rFIRST, r3 ); + } else { + MAYBE_EMIT_CONST(); + + in( iCMPWI, cr7, rFIRST, 0 ); + in( iBLTm, cr7, +4*5 /* syscall */ ); // XXX jump ! + /* instruction call */ + + // get instruction address + in( iLL, r0, VM_Data_Offset( iPointers ), rVMDATA ); + in( iRLWINM, rFIRST, rFIRST, GPRLEN_SHIFT, 0, 31-GPRLEN_SHIFT ); // mul * GPRLEN + in( iLLX, r0, rFIRST, r0 ); // load pointer + + in( iB, +4*(3 + (rFIRST != r3 ? 1 : 0) ) ); // XXX jump ! + + /* syscall */ + in( iLL, r0, VM_Data_Offset( AsmCall ), rVMDATA ); // get asmCall pointer + /* rFIRST can be r3 or some static register */ + if ( rFIRST != r3 ) + in( iMR, r3, rFIRST ); // push OPSTACK top value on argument list + in( iMR, r4, rPSTACK ); // push PSTACK on argument list + + /* common code */ + in( iMTCTR, r0 ); + in( iBCTRL ); + + if ( rFIRST != r3 ) + in( iMR, rFIRST, r3 ); // push return value on the top of the opstack + } + break; + + case OP_PUSH: + MAYBE_EMIT_CONST(); + if ( RET_INT ) + gpr_pos++; + else + fpr_pos++; + /* no instructions here */ + force_emit = 1; + break; + + case OP_POP: + MAYBE_EMIT_CONST(); + if ( ARG_INT ) + gpr_pos--; + else + fpr_pos--; + /* no instructions here */ + force_emit = 1; + break; + + case OP_CONST: + MAYBE_EMIT_CONST(); + /* nothing here */ + break; + + case OP_LOCAL: + MAYBE_EMIT_CONST(); + { + signed long int hi, lo; + hi = i_now->arg.ss[ 0 ]; + lo = i_now->arg.ss[ 1 ]; + if ( lo < 0 ) + hi += 1; + + gpr_pos++; + if ( hi == 0 ) { + in( iADDI, rFIRST, rPSTACK, lo ); + } else { + in( iADDIS, rFIRST, rPSTACK, hi ); + if ( lo != 0 ) + in( iADDI, rFIRST, rFIRST, lo ); + } + } + break; + + case OP_JUMP: + if ( i_const ) { + EMIT_FALSE_CONST(); + + emitJump( + i_const->arg.i, + branchAlways, 0, 0 + ); + } else { + MAYBE_EMIT_CONST(); + + in( iLL, r0, VM_Data_Offset( iPointers ), rVMDATA ); + in( iRLWINM, rFIRST, rFIRST, GPRLEN_SHIFT, 0, 31-GPRLEN_SHIFT ); // mul * GPRLEN + in( iLLX, r0, rFIRST, r0 ); // load pointer + in( iMTCTR, r0 ); + in( iBCTR ); + } + gpr_pos--; + break; + + case OP_EQ: + case OP_NE: + if ( i_const && i_const->arg.si >= -0x8000 && i_const->arg.si < 0x10000 ) { + EMIT_FALSE_CONST(); + if ( i_const->arg.si >= 0x8000 ) + in( iCMPLWI, cr7, rSECOND, i_const->arg.i ); + else + in( iCMPWI, cr7, rSECOND, i_const->arg.si ); + } else { + MAYBE_EMIT_CONST(); + in( iCMPW, cr7, rSECOND, rFIRST ); + } + emitJump( + i_now->arg.i, + (i_now->op == OP_EQ ? branchTrue : branchFalse), + 4*cr7+eq, 0 + ); + gpr_pos -= 2; + break; + + case OP_LTI: + case OP_GEI: + if ( i_const && i_const->arg.si >= -0x8000 && i_const->arg.si < 0x8000 ) { + EMIT_FALSE_CONST(); + in( iCMPWI, cr7, rSECOND, i_const->arg.si ); + } else { + MAYBE_EMIT_CONST(); + in( iCMPW, cr7, rSECOND, rFIRST ); + } + emitJump( + i_now->arg.i, + ( i_now->op == OP_LTI ? branchTrue : branchFalse ), + 4*cr7+lt, 0 + ); + gpr_pos -= 2; + break; + + case OP_GTI: + case OP_LEI: + if ( i_const && i_const->arg.si >= -0x8000 && i_const->arg.si < 0x8000 ) { + EMIT_FALSE_CONST(); + in( iCMPWI, cr7, rSECOND, i_const->arg.si ); + } else { + MAYBE_EMIT_CONST(); + in( iCMPW, cr7, rSECOND, rFIRST ); + } + emitJump( + i_now->arg.i, + ( i_now->op == OP_GTI ? branchTrue : branchFalse ), + 4*cr7+gt, 0 + ); + gpr_pos -= 2; + break; + + case OP_LTU: + case OP_GEU: + if ( i_const && i_const->arg.i < 0x10000 ) { + EMIT_FALSE_CONST(); + in( iCMPLWI, cr7, rSECOND, i_const->arg.i ); + } else { + MAYBE_EMIT_CONST(); + in( iCMPLW, cr7, rSECOND, rFIRST ); + } + emitJump( + i_now->arg.i, + ( i_now->op == OP_LTU ? branchTrue : branchFalse ), + 4*cr7+lt, 0 + ); + gpr_pos -= 2; + break; + + case OP_GTU: + case OP_LEU: + if ( i_const && i_const->arg.i < 0x10000 ) { + EMIT_FALSE_CONST(); + in( iCMPLWI, cr7, rSECOND, i_const->arg.i ); + } else { + MAYBE_EMIT_CONST(); + in( iCMPLW, cr7, rSECOND, rFIRST ); + } + emitJump( + i_now->arg.i, + ( i_now->op == OP_GTU ? branchTrue : branchFalse ), + 4*cr7+gt, 0 + ); + gpr_pos -= 2; + break; + + case OP_EQF: + case OP_NEF: + MAYBE_EMIT_CONST(); + in( iFCMPU, cr7, fSECOND, fFIRST ); + emitJump( + i_now->arg.i, + ( i_now->op == OP_EQF ? branchTrue : branchFalse ), + 4*cr7+eq, 0 + ); + fpr_pos -= 2; + break; + + case OP_LTF: + case OP_GEF: + MAYBE_EMIT_CONST(); + in( iFCMPU, cr7, fSECOND, fFIRST ); + emitJump( + i_now->arg.i, + ( i_now->op == OP_LTF ? branchTrue : branchFalse ), + 4*cr7+lt, 0 + ); + fpr_pos -= 2; + break; + + case OP_GTF: + case OP_LEF: + MAYBE_EMIT_CONST(); + in( iFCMPU, cr7, fSECOND, fFIRST ); + emitJump( + i_now->arg.i, + ( i_now->op == OP_GTF ? branchTrue : branchFalse ), + 4*cr7+gt, 0 + ); + fpr_pos -= 2; + break; + + case OP_LOAD1: + MAYBE_EMIT_CONST(); +#if OPTIMIZE_MASK + in( iRLWINM, rFIRST, rFIRST, 0, fastMaskHi, fastMaskLo ); +#else + in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA ); + in( iAND, rFIRST, rFIRST, r0 ); +#endif + in( iLBZX, rFIRST, rFIRST, rDATABASE ); + break; + + case OP_LOAD2: + MAYBE_EMIT_CONST(); +#if OPTIMIZE_MASK + in( iRLWINM, rFIRST, rFIRST, 0, fastMaskHi, fastMaskLo ); +#else + in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA ); + in( iAND, rFIRST, rFIRST, r0 ); +#endif + in( iLHZX, rFIRST, rFIRST, rDATABASE ); + break; + + case OP_LOAD4: + MAYBE_EMIT_CONST(); +#if OPTIMIZE_MASK + in( iRLWINM, rFIRST, rFIRST, 0, fastMaskHi, fastMaskLo ); +#else + in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA ); + in( iAND, rFIRST, rFIRST, r0 ); +#endif + if ( RET_INT ) { + in( iLWZX, rFIRST, rFIRST, rDATABASE ); + } else { + fpr_pos++; + in( iLFSX, fFIRST, rFIRST, rDATABASE ); + gpr_pos--; + } + break; + + case OP_STORE1: + MAYBE_EMIT_CONST(); +#if OPTIMIZE_MASK + in( iRLWINM, rSECOND, rSECOND, 0, fastMaskHi, fastMaskLo ); +#else + in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA ); + in( iAND, rSECOND, rSECOND, r0 ); +#endif + in( iSTBX, rFIRST, rSECOND, rDATABASE ); + gpr_pos -= 2; + break; + + case OP_STORE2: + MAYBE_EMIT_CONST(); +#if OPTIMIZE_MASK + in( iRLWINM, rSECOND, rSECOND, 0, fastMaskHi, fastMaskLo ); +#else + in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA ); + in( iAND, rSECOND, rSECOND, r0 ); +#endif + in( iSTHX, rFIRST, rSECOND, rDATABASE ); + gpr_pos -= 2; + break; + + case OP_STORE4: + MAYBE_EMIT_CONST(); + if ( ARG_INT ) { +#if OPTIMIZE_MASK + in( iRLWINM, rSECOND, rSECOND, 0, fastMaskHi, fastMaskLo ); +#else + in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA ); + in( iAND, rSECOND, rSECOND, r0 ); +#endif + + in( iSTWX, rFIRST, rSECOND, rDATABASE ); + gpr_pos--; + } else { +#if OPTIMIZE_MASK + in( iRLWINM, rFIRST, rFIRST, 0, fastMaskHi, fastMaskLo ); +#else + in( iLWZ, r0, VM_Data_Offset( dataMask ), rVMDATA ); + in( iAND, rFIRST, rFIRST, r0 ); +#endif + + in( iSTFSX, fFIRST, rFIRST, rDATABASE ); + fpr_pos--; + } + gpr_pos--; + break; + + case OP_ARG: + MAYBE_EMIT_CONST(); + in( iADDI, r0, rPSTACK, i_now->arg.b ); + if ( ARG_INT ) { + in( iSTWX, rFIRST, rDATABASE, r0 ); + gpr_pos--; + } else { + in( iSTFSX, fFIRST, rDATABASE, r0 ); + fpr_pos--; + } + break; + + case OP_BLOCK_COPY: + MAYBE_EMIT_CONST(); +#if OPTIMIZE_COPY + if ( i_now->arg.i <= SL( 16, 32 ) ) { + /* block is very short so copy it in-place */ + + unsigned int len = i_now->arg.i; + unsigned int copied = 0, left = len; + + in( iADD, rFIRST, rFIRST, rDATABASE ); + in( iADD, rSECOND, rSECOND, rDATABASE ); + + if ( len >= GPRLEN ) { + long int i, words = len / GPRLEN; + in( iLL, r0, 0, rFIRST ); + for ( i = 1; i < words; i++ ) + in( iLL, rTEMP( i - 1 ), GPRLEN * i, rFIRST ); + + in( iSTL, r0, 0, rSECOND ); + for ( i = 1; i < words; i++ ) + in( iSTL, rTEMP( i - 1 ), GPRLEN * i, rSECOND ); + + copied += words * GPRLEN; + left -= copied; + } + + if ( SL( 0, left >= 4 ) ) { + in( iLWZ, r0, copied+0, rFIRST ); + in( iSTW, r0, copied+0, rSECOND ); + copied += 4; + left -= 4; + } + if ( left >= 4 ) { + DIE("Bug in OP_BLOCK_COPY"); + } + if ( left == 3 ) { + in( iLHZ, r0, copied+0, rFIRST ); + in( iLBZ, rTMP, copied+2, rFIRST ); + in( iSTH, r0, copied+0, rSECOND ); + in( iSTB, rTMP, copied+2, rSECOND ); + } else if ( left == 2 ) { + in( iLHZ, r0, copied+0, rFIRST ); + in( iSTH, r0, copied+0, rSECOND ); + } else if ( left == 1 ) { + in( iLBZ, r0, copied+0, rFIRST ); + in( iSTB, r0, copied+0, rSECOND ); + } + } else +#endif + { + unsigned long int r5_ori = 0; + if ( i_now->arg.si >= -0x8000 && i_now->arg.si < 0x8000 ) { + in( iLI, r5, i_now->arg.si ); + } else if ( i_now->arg.i < 0x10000 ) { + in( iLI, r5, 0 ); + r5_ori = i_now->arg.i; + } else { + in( iLIS, r5, i_now->arg.ss[ 0 ] ); + r5_ori = i_now->arg.us[ 1 ]; + } + + in( iLL, r0, VM_Data_Offset( BlockCopy ), rVMDATA ); // get blockCopy pointer + + if ( r5_ori ) + in( iORI, r5, r5, r5_ori ); + + in( iMTCTR, r0 ); + + if ( rFIRST != r4 ) + in( iMR, r4, rFIRST ); + if ( rSECOND != r3 ) + in( iMR, r3, rSECOND ); + + in( iBCTRL ); + } + + gpr_pos -= 2; + break; + + case OP_SEX8: + MAYBE_EMIT_CONST(); + in( iEXTSB, rFIRST, rFIRST ); + break; + + case OP_SEX16: + MAYBE_EMIT_CONST(); + in( iEXTSH, rFIRST, rFIRST ); + break; + + case OP_NEGI: + MAYBE_EMIT_CONST(); + in( iNEG, rFIRST, rFIRST ); + break; + + case OP_ADD: + if ( i_const ) { + EMIT_FALSE_CONST(); + + signed short int hi, lo; + hi = i_const->arg.ss[ 0 ]; + lo = i_const->arg.ss[ 1 ]; + if ( lo < 0 ) + hi += 1; + + if ( hi != 0 ) + in( iADDIS, rSECOND, rSECOND, hi ); + if ( lo != 0 ) + in( iADDI, rSECOND, rSECOND, lo ); + + // if both are zero no instruction will be written + if ( hi == 0 && lo == 0 ) + force_emit = 1; + } else { + MAYBE_EMIT_CONST(); + in( iADD, rSECOND, rSECOND, rFIRST ); + } + gpr_pos--; + break; + + case OP_SUB: + MAYBE_EMIT_CONST(); + in( iSUB, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_DIVI: + MAYBE_EMIT_CONST(); + in( iDIVW, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_DIVU: + MAYBE_EMIT_CONST(); + in( iDIVWU, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_MODI: + MAYBE_EMIT_CONST(); + in( iDIVW, r0, rSECOND, rFIRST ); + in( iMULLW, r0, r0, rFIRST ); + in( iSUB, rSECOND, rSECOND, r0 ); + gpr_pos--; + break; + + case OP_MODU: + MAYBE_EMIT_CONST(); + in( iDIVWU, r0, rSECOND, rFIRST ); + in( iMULLW, r0, r0, rFIRST ); + in( iSUB, rSECOND, rSECOND, r0 ); + gpr_pos--; + break; + + case OP_MULI: + case OP_MULU: + MAYBE_EMIT_CONST(); + in( iMULLW, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_BAND: + MAYBE_EMIT_CONST(); + in( iAND, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_BOR: + MAYBE_EMIT_CONST(); + in( iOR, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_BXOR: + MAYBE_EMIT_CONST(); + in( iXOR, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_BCOM: + MAYBE_EMIT_CONST(); + in( iNOT, rFIRST, rFIRST ); + break; + + case OP_LSH: + MAYBE_EMIT_CONST(); + in( iSLW, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_RSHI: + MAYBE_EMIT_CONST(); + in( iSRAW, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_RSHU: + MAYBE_EMIT_CONST(); + in( iSRW, rSECOND, rSECOND, rFIRST ); + gpr_pos--; + break; + + case OP_NEGF: + MAYBE_EMIT_CONST(); + in( iFNEG, fFIRST, fFIRST ); + break; + + case OP_ADDF: + MAYBE_EMIT_CONST(); + in( iFADDS, fSECOND, fSECOND, fFIRST ); + fpr_pos--; + break; + + case OP_SUBF: + MAYBE_EMIT_CONST(); + in( iFSUBS, fSECOND, fSECOND, fFIRST ); + fpr_pos--; + break; + + case OP_DIVF: + MAYBE_EMIT_CONST(); + in( iFDIVS, fSECOND, fSECOND, fFIRST ); + fpr_pos--; + break; + + case OP_MULF: + MAYBE_EMIT_CONST(); + in( iFMULS, fSECOND, fSECOND, fFIRST ); + fpr_pos--; + break; + + case OP_CVIF: + MAYBE_EMIT_CONST(); + fpr_pos++; + in( iXORIS, rFIRST, rFIRST, 0x8000 ); + in( iLIS, r0, 0x4330 ); + in( iSTW, rFIRST, stack_temp + 4, r1 ); + in( iSTW, r0, stack_temp, r1 ); + in( iLFS, fTMP, VM_Data_Offset( floatBase ), rVMDATA ); + in( iLFD, fFIRST, stack_temp, r1 ); + in( iFSUB, fFIRST, fFIRST, fTMP ); + in( iFRSP, fFIRST, fFIRST ); + gpr_pos--; + break; + + case OP_CVFI: + MAYBE_EMIT_CONST(); + gpr_pos++; + in( iFCTIWZ, fFIRST, fFIRST ); + in( iSTFD, fFIRST, stack_temp, r1 ); + in( iLWZ, rFIRST, stack_temp + 4, r1 ); + fpr_pos--; + break; + } + + i_const = NULL; + + if ( i_now->op != OP_CONST ) { + // emit the instructions if it isn't OP_CONST + emitEnd(); + } else { + // mark in what register the value should be saved + if ( RET_INT ) + i_now->regPos = ++gpr_pos; + else + i_now->regPos = ++fpr_pos; + +#if OPTIMIZE_HOLE + i_const = i_now; +#else + PPC_EmitConst( i_now ); +#endif + } + } + if ( i_const ) + DIE( "left (unused) OP_CONST" ); + + { + // free opcode information, don't free first dummy one + source_instruction_t *i_next = i_first->next; + while ( i_next ) { + i_now = i_next; + i_next = i_now->next; + PPC_Free( i_now ); + } + } +} + + +/* + * check which jumps are short enough to use signed 16bit immediate branch + */ +static void +PPC_ShrinkJumps( void ) +{ + symbolic_jump_t *sj_now = sj_first; + while ( (sj_now = sj_now->nextJump) ) { + if ( sj_now->bo == branchAlways ) + // non-conditional branch has 26bit immediate + sj_now->parent->length = 1; + + else { + dest_instruction_t *di = di_pointers[ sj_now->jump_to ]; + dest_instruction_t *ji = sj_now->parent; + long int jump_length = 0; + if ( ! di ) + DIE( "No instruction to jump to" ); + if ( ji->count > di->count ) { + do { + jump_length += di->length; + } while ( ( di = di->next ) != ji ); + } else { + jump_length = 1; + while ( ( ji = ji->next ) != di ) + jump_length += ji->length; + } + if ( jump_length < 0x2000 ) + // jump is short, use normal instruction + sj_now->parent->length = 1; + } + } +} + +/* + * puts all the data in one place, it consists of many different tasks + */ +static void +PPC_ComputeCode( vm_t *vm ) +{ + dest_instruction_t *di_now = di_first; + + unsigned long int codeInstructions = 0; + // count total instruciton number + while ( (di_now = di_now->next ) ) + codeInstructions += di_now->length; + + size_t codeLength = sizeof( vm_data_t ) + + sizeof( unsigned int ) * data_acc + + sizeof( ppc_instruction_t ) * codeInstructions; + + // get the memory for the generated code, smarter ppcs need the + // mem to be marked as executable (whill change later) + unsigned char *dataAndCode = mmap( NULL, codeLength, + PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0 ); + + if ( ! dataAndCode ) + DIE( "Not enough memory" ); + + ppc_instruction_t *codeNow, *codeBegin; + codeNow = codeBegin = (ppc_instruction_t *)( dataAndCode + VM_Data_Offset( data[ data_acc ] ) ); + + ppc_instruction_t nop = IN( iNOP ); + + // copy instructions to the destination + // fills the jump instructions with nops + // saves pointers of all instructions + di_now = di_first; + while ( (di_now = di_now->next ) ) { + unsigned long int i_count = di_now->i_count; + if ( i_count != FALSE_ICOUNT ) { + if ( ! di_pointers[ i_count ] ) + di_pointers[ i_count ] = (void *) codeNow; + } + + if ( di_now->jump == NULL ) { + memcpy( codeNow, &(di_now->code[0]), di_now->length * sizeof( ppc_instruction_t ) ); + codeNow += di_now->length; + } else { + long int i; + symbolic_jump_t *sj; + for ( i = 0; i < di_now->length; i++ ) + codeNow[ i ] = nop; + codeNow += di_now->length; + + sj = di_now->jump; + // save position of jumping instruction + sj->parent = (void *)(codeNow - 1); + } + } + + // compute the jumps and write corresponding instructions + symbolic_jump_t *sj_now = sj_first; + while ( (sj_now = sj_now->nextJump ) ) { + ppc_instruction_t *jumpFrom = (void *) sj_now->parent; + ppc_instruction_t *jumpTo = (void *) di_pointers[ sj_now->jump_to ]; + signed long int jumpLength = jumpTo - jumpFrom; + + // if jump is short, just write it + if ( jumpLength >= - 8192 && jumpLength < 8192 ) { + powerpc_iname_t branchConditional = sj_now->ext & branchExtLink ? iBCL : iBC; + *jumpFrom = IN( branchConditional, sj_now->bo, sj_now->bi, jumpLength * 4 ); + continue; + } + + // jump isn't short so write it as two instructions + // + // the letter one is a non-conditional branch instruction which + // accepts immediate values big enough (26 bits) + *jumpFrom = IN( (sj_now->ext & branchExtLink ? iBL : iB), jumpLength * 4 ); + if ( sj_now->bo == branchAlways ) + continue; + + // there should have been additional space prepared for this case + if ( jumpFrom[ -1 ] != nop ) + DIE( "additional space for long jump not prepared" ); + + // invert instruction condition + long int bo = 0; + switch ( sj_now->bo ) { + case branchTrue: + bo = branchFalse; + break; + case branchFalse: + bo = branchTrue; + break; + default: + DIE( "unrecognized branch type" ); + break; + } + + // the former instruction is an inverted conditional branch which + // jumps over the non-conditional one + jumpFrom[ -1 ] = IN( iBC, bo, sj_now->bi, +2*4 ); + } + + vm->codeBase = dataAndCode; + vm->codeLength = codeLength; + + vm_data_t *data = (vm_data_t *)dataAndCode; + +#if ELF64 + // prepare Official Procedure Descriptor for the generated code + // and retrieve real function pointer for helper functions + + opd_t *ac = (void *)VM_AsmCall, *bc = (void *)VM_BlockCopy; + data->opd.function = codeBegin; + // trick it into using the same TOC + // this way we won't have to switch TOC before calling AsmCall or BlockCopy + data->opd.toc = ac->toc; + data->opd.env = ac->env; + + data->AsmCall = ac->function; + data->BlockCopy = bc->function; +#else + data->AsmCall = VM_AsmCall; + data->BlockCopy = VM_BlockCopy; +#endif + + data->dataMask = vm->dataMask; + data->iPointers = (ppc_instruction_t *)vm->instructionPointers; + data->dataLength = VM_Data_Offset( data[ data_acc ] ); + data->codeLength = ( codeNow - codeBegin ) * sizeof( ppc_instruction_t ); + data->floatBase = 0x59800004; + + + /* write dynamic data (float constants) */ + { + local_data_t *d_next, *d_now = data_first; + long int accumulated = 0; + + do { + long int i; + for ( i = 0; i < d_now->count; i++ ) + data->data[ accumulated + i ] = d_now->data[ i ]; + + accumulated += d_now->count; + d_next = d_now->next; + PPC_Free( d_now ); + + if ( !d_next ) + break; + d_now = d_next; + } while (1); + data_first = NULL; + } + + /* free most of the compilation memory */ + { + di_now = di_first->next; + PPC_Free( di_first ); + PPC_Free( sj_first ); + + while ( di_now ) { + di_first = di_now->next; + if ( di_now->jump ) + PPC_Free( di_now->jump ); + PPC_Free( di_now ); + di_now = di_first; + } + } + + return; +} + +static void +VM_Destroy_Compiled( vm_t *self ) +{ + if ( self->codeBase ) { + if ( munmap( self->codeBase, self->codeLength ) ) + Com_Printf( S_COLOR_RED "Memory unmap failed, possible memory leak\n" ); + } + self->codeBase = NULL; +} + +void +VM_Compile( vm_t *vm, vmHeader_t *header ) +{ + long int pc = 0; + unsigned long int i_count; + char* code; + struct timeval tvstart = {0, 0}; + source_instruction_t *i_first /* dummy */, *i_last = NULL, *i_now; + + vm->compiled = qfalse; + + gettimeofday(&tvstart, NULL); + + PPC_MakeFastMask( vm->dataMask ); + + i_first = PPC_Malloc( sizeof( source_instruction_t ) ); + i_first->next = NULL; + + // realloc instructionPointers with correct size + // use Z_Malloc so vm.c will be able to free the memory + if ( sizeof( void * ) != sizeof( int ) ) { + Z_Free( vm->instructionPointers ); + vm->instructionPointers = Z_Malloc( header->instructionCount * sizeof( void * ) ); + } + di_pointers = (void *)vm->instructionPointers; + memset( di_pointers, 0, header->instructionCount * sizeof( void * ) ); + + + PPC_CompileInit(); + + /* + * read the input program + * divide it into functions and send each function to compiler + */ + code = (char *)header + header->codeOffset; + for ( i_count = 0; i_count < header->instructionCount; ++i_count ) + { + unsigned char op = code[ pc++ ]; + + if ( op == OP_ENTER ) { + if ( i_first->next ) + VM_CompileFunction( i_first ); + i_first->next = NULL; + i_last = i_first; + } + + i_now = PPC_Malloc( sizeof( source_instruction_t ) ); + i_now->op = op; + i_now->i_count = i_count; + i_now->arg.i = 0; + i_now->regA1 = 0; + i_now->regA2 = 0; + i_now->regR = 0; + i_now->regPos = 0; + i_now->next = NULL; + + if ( vm_opInfo[op] & opImm4 ) { + union { + unsigned char b[4]; + unsigned int i; + } c = { { code[ pc + 3 ], code[ pc + 2 ], code[ pc + 1 ], code[ pc + 0 ] }, }; + + i_now->arg.i = c.i; + pc += 4; + } else if ( vm_opInfo[op] & opImm1 ) { + i_now->arg.b = code[ pc++ ]; + } + + i_last->next = i_now; + i_last = i_now; + } + VM_CompileFunction( i_first ); + PPC_Free( i_first ); + + PPC_ShrinkJumps(); + memset( di_pointers, 0, header->instructionCount * sizeof( void * ) ); + PPC_ComputeCode( vm ); + + /* check for uninitialized pointers */ +#ifdef DEBUG_VM + long int i; + for ( i = 0; i < header->instructionCount; i++ ) + if ( di_pointers[ i ] == 0 ) + Com_Printf( S_COLOR_RED "Pointer %ld not initialized !\n", i ); +#endif + + /* mark memory as executable and not writeable */ + if ( mprotect( vm->codeBase, vm->codeLength, PROT_READ|PROT_EXEC ) ) { + + // it has failed, make sure memory is unmapped before throwing the error + VM_Destroy_Compiled( vm ); + DIE( "mprotect failed" ); + } + + vm->destroy = VM_Destroy_Compiled; + vm->compiled = qtrue; + + { + struct timeval tvdone = {0, 0}; + struct timeval dur = {0, 0}; + Com_Printf( "VM file %s compiled to %i bytes of code (%p - %p)\n", + vm->name, vm->codeLength, vm->codeBase, vm->codeBase+vm->codeLength ); + + gettimeofday(&tvdone, NULL); + timersub(&tvdone, &tvstart, &dur); + Com_Printf( "compilation took %lu.%06lu seconds\n", + (long unsigned int)dur.tv_sec, (long unsigned int)dur.tv_usec ); + } +} + +int +VM_CallCompiled( vm_t *vm, int *args ) +{ + int retVal; + int *argPointer; + + vm_data_t *vm_dataAndCode = (void *)( vm->codeBase ); + int programStack = vm->programStack; + int stackOnEntry = programStack; + + byte *image = vm->dataBase; + + currentVM = vm; + + vm->currentlyInterpreting = qtrue; + + programStack -= 48; + argPointer = (int *)&image[ programStack + 8 ]; + memcpy( argPointer, args, 4 * 9 ); + argPointer[ -1 ] = 0; + argPointer[ -2 ] = -1; + +#ifdef VM_TIMES + struct tms start_time, stop_time; + clock_t time_diff; + + times( &start_time ); + time_outside_vm = 0; +#endif + + /* call generated code */ + { + int ( *entry )( void *, int, void * ); +#ifdef __PPC64__ + entry = (void *)&(vm_dataAndCode->opd); +#else + entry = (void *)(vm->codeBase + vm_dataAndCode->dataLength); +#endif + retVal = entry( vm->codeBase, programStack, vm->dataBase ); + } + +#ifdef VM_TIMES + times( &stop_time ); + time_diff = stop_time.tms_utime - start_time.tms_utime; + time_total_vm += time_diff - time_outside_vm; + if ( time_diff > 100 ) { + printf( "App clock: %ld, vm total: %ld, vm this: %ld, vm real: %ld, vm out: %ld\n" + "Inside VM %f%% of app time\n", + stop_time.tms_utime, + time_total_vm, + time_diff, + time_diff - time_outside_vm, + time_outside_vm, + (double)100 * time_total_vm / stop_time.tms_utime ); + } +#endif + + vm->programStack = stackOnEntry; + vm->currentlyInterpreting = qfalse; + + return retVal; +} diff --git a/src/qcommon/vm_powerpc_asm.c b/src/qcommon/vm_powerpc_asm.c new file mode 100644 index 00000000..64c59771 --- /dev/null +++ b/src/qcommon/vm_powerpc_asm.c @@ -0,0 +1,1009 @@ +/* +=========================================================================== +Copyright (C) 2008 Przemyslaw Iskra + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== + + * File includes code from GNU binutils, exactly: + * - include/opcode/ppc.h - licensed under GPL v1 or later + * - opcodes/ppc-opc.c - licensed under GPL v2 or later + * + * ppc.h -- Header file for PowerPC opcode table + * Copyright 1994, 1995, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + * 2007 Free Software Foundation, Inc. + * Written by Ian Lance Taylor, Cygnus Suppor + * + * This file is part of GDB, GAS, and the GNU binutils. + * + * ppc-opc.c -- PowerPC opcode list + * Copyright 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, + * 2005, 2006, 2007 Free Software Foundation, Inc. + * Written by Ian Lance Taylor, Cygnus Support + * + * This file is part of GDB, GAS, and the GNU binutils. + * + */ + +#include "vm_powerpc_asm.h" + +#include +#include +#include + +/* return nop on error */ +#define ASM_ERROR_OPC (0x60000000) + +/* + * BEGIN OF ppc.h + */ + +#define ppc_cpu_t int + +struct powerpc_opcode +{ + const char *name; + unsigned long opcode; + unsigned long mask; + ppc_cpu_t flags; + unsigned char operands[8]; +}; + +static const struct powerpc_opcode powerpc_opcodes[]; +static const int powerpc_num_opcodes; + +#define PPC_OPCODE_PPC 1 +#define PPC_OPCODE_POWER 2 +#define PPC_OPCODE_POWER2 4 +#define PPC_OPCODE_32 8 +#define PPC_OPCODE_64 0x10 +#define PPC_OPCODE_601 0x20 +#define PPC_OPCODE_COMMON 0x40 +#define PPC_OPCODE_ANY 0x80 +#define PPC_OPCODE_64_BRIDGE 0x100 +#define PPC_OPCODE_ALTIVEC 0x200 +#define PPC_OPCODE_403 0x400 +#define PPC_OPCODE_BOOKE 0x800 +#define PPC_OPCODE_BOOKE64 0x1000 +#define PPC_OPCODE_440 0x2000 +#define PPC_OPCODE_POWER4 0x4000 +#define PPC_OPCODE_NOPOWER4 0x8000 +#define PPC_OPCODE_CLASSIC 0x10000 +#define PPC_OPCODE_SPE 0x20000 +#define PPC_OPCODE_ISEL 0x40000 +#define PPC_OPCODE_EFS 0x80000 +#define PPC_OPCODE_BRLOCK 0x100000 +#define PPC_OPCODE_PMR 0x200000 +#define PPC_OPCODE_CACHELCK 0x400000 +#define PPC_OPCODE_RFMCI 0x800000 +#define PPC_OPCODE_POWER5 0x1000000 +#define PPC_OPCODE_E300 0x2000000 +#define PPC_OPCODE_POWER6 0x4000000 +#define PPC_OPCODE_CELL 0x8000000 +#define PPC_OPCODE_PPCPS 0x10000000 +#define PPC_OPCODE_E500MC 0x20000000 +#define PPC_OPCODE_405 0x40000000 +#define PPC_OPCODE_VSX 0x80000000 + +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +struct powerpc_operand +{ + unsigned int bitm; + int shift; + unsigned long (*insert) + (unsigned long, long, int, const char **); + unsigned long flags; +}; + +static const struct powerpc_operand powerpc_operands[]; +static const unsigned int num_powerpc_operands; + +#define PPC_OPERAND_SIGNED (0x1) +#define PPC_OPERAND_SIGNOPT (0x2) +#define PPC_OPERAND_FAKE (0x4) +#define PPC_OPERAND_PARENS (0x8) +#define PPC_OPERAND_CR (0x10) +#define PPC_OPERAND_GPR (0x20) +#define PPC_OPERAND_GPR_0 (0x40) +#define PPC_OPERAND_FPR (0x80) +#define PPC_OPERAND_RELATIVE (0x100) +#define PPC_OPERAND_ABSOLUTE (0x200) +#define PPC_OPERAND_OPTIONAL (0x400) +#define PPC_OPERAND_NEXT (0x800) +#define PPC_OPERAND_NEGATIVE (0x1000) +#define PPC_OPERAND_VR (0x2000) +#define PPC_OPERAND_DS (0x4000) +#define PPC_OPERAND_DQ (0x8000) +#define PPC_OPERAND_PLUS1 (0x10000) +#define PPC_OPERAND_FSL (0x20000) +#define PPC_OPERAND_FCR (0x40000) +#define PPC_OPERAND_UDI (0x80000) +#define PPC_OPERAND_VSR (0x100000) + +/* + * END OF ppc.h + */ + +#define PPC_DEST_ARCH PPC_OPCODE_PPC + +ppc_instruction_t +asm_instruction( powerpc_iname_t sname, const int argc, const long int *argv ) +{ + const char *errmsg = NULL; + const char *name; + unsigned long int ret; + const struct powerpc_opcode *opcode = NULL; + int argi, argj; + + opcode = &powerpc_opcodes[ sname ]; + name = opcode->name; + + if ( ! opcode ) { + printf( "Can't find opcode %d\n", sname ); + return ASM_ERROR_OPC; + } + if ( ( opcode->flags & PPC_DEST_ARCH ) != PPC_DEST_ARCH ) { + printf( "opcode %s not defined for this arch\n", name ); + return ASM_ERROR_OPC; + } + + ret = opcode->opcode; + + argi = argj = 0; + while ( opcode->operands[ argi ] != 0 ) { + long int op = 0; + const struct powerpc_operand *operand = &powerpc_operands[ opcode->operands[ argi ] ]; + + if ( ! (operand->flags & PPC_OPERAND_FAKE) ) { + if ( argj >= argc ) { + printf( "Not enough arguments for %s, got %d\n", name, argc ); + return ASM_ERROR_OPC; + } + + op = argv[ argj++ ]; + } + + if ( operand->insert ) { + errmsg = NULL; + ret = operand->insert( ret, op, PPC_DEST_ARCH, &errmsg ); + if ( errmsg ) { + printf( "%s: error while inserting operand %d (0x%.2lx): %s\n", + name, argi, op, errmsg ); + } + } else { + unsigned long int opu = *(unsigned long int *)&op; + unsigned long int bitm = operand->bitm; + unsigned long int bitm_full = bitm | ( bitm & 1 ? 0 : 0xf ); + + if ( operand->flags & PPC_OPERAND_SIGNED ) { + bitm_full >>= 1; + + if ( ( opu & ~bitm_full ) != 0 && ( opu | bitm_full ) != -1 ) + printf( "%s: signed operand nr.%d to wide. op: %.8lx, mask: %.8lx\n", + name, argi, opu, bitm ); + } else { + if ( ( opu & ~bitm_full ) != 0 ) + printf( "%s: unsigned operand nr.%d to wide. op: %.8lx, mask: %.8lx\n", + name, argi, opu, bitm ); + } + if ( (bitm & 1) == 0 ) { + if ( opu & 0xf & ~bitm ) + printf( "%s: operand nr.%d not aligned correctly. op: %.8lx, mask: %.8lx\n", + name, argi, opu, bitm ); + } + + ret |= ( op & operand->bitm ) << operand->shift; + } + argi++; + } + if ( argc > argj ) { + printf( "Too many arguments for %s, got %d\n", name, argc ); + return ASM_ERROR_OPC; + } + + return ret; +} + + +/* + * BEGIN OF ppc-opc.c + */ + +#define ATTRIBUTE_UNUSED +#define _(x) (x) + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bdm (unsigned long, long, int, const char **); +static unsigned long insert_bo (unsigned long, long, int, const char **); +static unsigned long insert_ras (unsigned long, long, int, const char **); +static unsigned long insert_rbs (unsigned long, long, int, const char **); + +/* The operands table. + + The fields are bitm, shift, insert, extract, flags. + */ + +static const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED 0 + { 0, 0, NULL, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA UNUSED + 1 + /* The BI field in a B form or XL form instruction. */ +#define BI BA +#define BI_MASK (0x1f << 16) + { 0x1f, 16, NULL, PPC_OPERAND_CR }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD BA + 1 + { 0xfffc, 0, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM BD + 1 + { 0xfffc, 0, insert_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF BDM + 1 + /* The CRFD field in an X form instruction. */ +#define CRFD BF + { 0x7, 23, NULL, PPC_OPERAND_CR }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF BF + 1 + { 0x7, 23, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO OBF + 1 +#define BO_MASK (0x1f << 21) + { 0x1f, 21, insert_bo, 0 }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR BO + 1 + { 0x7, 18, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D CR + 1 + { 0xffff, 0, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#define DS D + 1 + { 0xfffc, 0, NULL, + PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED | PPC_OPERAND_DS }, + + /* The FRA field in an X or A form instruction. */ +#define FRA DS + 1 +#define FRA_MASK (0x1f << 16) + { 0x1f, 16, NULL, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB FRA + 1 +#define FRB_MASK (0x1f << 11) + { 0x1f, 11, NULL, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC FRB + 1 +#define FRC_MASK (0x1f << 6) + { 0x1f, 6, NULL, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS FRC + 1 +#define FRT FRS + { 0x1f, 21, NULL, PPC_OPERAND_FPR }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI FRS + 1 + { 0x3fffffc, 0, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The ME field in an M form instruction. */ +#define ME LI + 1 +#define ME_MASK (0x1f << 1) + { 0x1f, 1, NULL, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE ME + 1 + { 0x1f, 6, NULL, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + + /* The RA field in an D, DS, DQ, X, XO, M, or MDS form instruction. */ +#define RA MBE + 1 +#define RA_MASK (0x1f << 16) + { 0x1f, 16, NULL, PPC_OPERAND_GPR }, + + /* As above, but 0 in the RA field means zero, not r0. */ +#define RA0 RA + 1 + { 0x1f, 16, NULL, PPC_OPERAND_GPR_0 }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS RA0 + 1 + { 0x1f, 16, insert_ras, PPC_OPERAND_GPR_0 }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB RAS + 1 +#define RB_MASK (0x1f << 11) + { 0x1f, 11, NULL, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS RB + 1 + { 0x1f, 11, insert_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS RBS + 1 +#define RT RS +#define RT_MASK (0x1f << 21) + { 0x1f, 21, NULL, PPC_OPERAND_GPR }, + + /* The SH field in an X or M form instruction. */ +#define SH RS + 1 +#define SH_MASK (0x1f << 11) + /* The other UIMM field in a EVX form instruction. */ +#define EVUIMM SH + { 0x1f, 11, NULL, 0 }, + + /* The SI field in a D form instruction. */ +#define SI SH + 1 + { 0xffff, 0, NULL, PPC_OPERAND_SIGNED }, + + /* The UI field in a D form instruction. */ +#define UI SI + 1 + { 0xffff, 0, NULL, 0 }, + +}; + +static const unsigned int num_powerpc_operands = + (sizeof (powerpc_operands) / sizeof (powerpc_operands[0])); + +/* The functions used to insert and extract complicated operands. */ + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + For chips built to versions of the architecture prior to version 2 + (ie. not Power4 compatible), we set the y bit of the BO field to 1 + if the offset is negative. When extracting, we require that the y + bit be 1 and that the offset be positive, since if the y bit is 0 + we just want to print the normal form of the instruction. + Power4 compatible targets use two bits, "a", and "t", instead of + the "y" bit. "at" == 00 => no hint, "at" == 01 => unpredictable, + "at" == 10 => not taken, "at" == 11 => taken. The "t" bit is 00001 + in BO field, the "a" bit is 00010 for branch on CR(BI) and 01000 + for branch on CTR. We only handle the taken/not-taken hint here. + Note that we don't relax the conditions tested here when + disassembling with -Many because insns using extract_bdm and + extract_bdp always occur in pairs. One or the other will always + be valid. */ + +static unsigned long +insert_bdm (unsigned long insn, + long value, + int dialect, + const char **errmsg ATTRIBUTE_UNUSED) +{ + if ((dialect & PPC_OPCODE_POWER4) == 0) + { + if ((value & 0x8000) != 0) + insn |= 1 << 21; + } + else + { + if ((insn & (0x14 << 21)) == (0x04 << 21)) + insn |= 0x02 << 21; + else if ((insn & (0x14 << 21)) == (0x10 << 21)) + insn |= 0x08 << 21; + } + return insn | (value & 0xfffc); +} + + +/* Check for legal values of a BO field. */ + +static int +valid_bo (long value, int dialect, int extract) +{ + if ((dialect & PPC_OPCODE_POWER4) == 0) + { + int valid; + /* Certain encodings have bits that are required to be zero. + These are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + valid = 1; + break; + case 0x4: + valid = (value & 0x2) == 0; + break; + case 0x10: + valid = (value & 0x8) == 0; + break; + case 0x14: + valid = value == 0x14; + break; + } + /* When disassembling with -Many, accept power4 encodings too. */ + if (valid + || (dialect & PPC_OPCODE_ANY) == 0 + || !extract) + return valid; + } + + /* Certain encodings have bits that are required to be zero. + These are (z must be zero, a & t may be anything): + 0000z + 0001z + 0100z + 0101z + 001at + 011at + 1a00t + 1a01t + 1z1zz + */ + if ((value & 0x14) == 0) + return (value & 0x1) == 0; + else if ((value & 0x14) == 0x14) + return value == 0x14; + else + return 1; +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo (unsigned long insn, + long value, + int dialect, + const char **errmsg) +{ + if (!valid_bo (value, dialect, 0)) + *errmsg = _("invalid conditional option"); + return insn | ((value & 0x1f) << 21); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras (unsigned long insn, + long value, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg) +{ + if (value == 0) + *errmsg = _("invalid register operand when updating"); + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +static unsigned long +insert_rbs (unsigned long insn, + long value ATTRIBUTE_UNUSED, + int dialect ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) ((((unsigned long)(x)) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | ((((unsigned long)(to)) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | ((((unsigned long)(l)) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1) | (((unsigned long)(rc)) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* An AFRAFRC_MASK, but with L bit clear. */ +#define AFRALFRC_MASK (AFRAFRC_MASK & ~((unsigned long) 1 << 16)) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | ((((unsigned long)(aa)) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. Similarly for the 'at' bits used for power4 branch hints. */ +#define Y_MASK (((unsigned long) 1) << 21) +#define AT1_MASK (((unsigned long) 3) << 21) +#define AT2_MASK (((unsigned long) 9) << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) +#define BBOAT_MASK (BBO_MASK &~ AT1_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | ((((unsigned long)(cb)) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) +#define BBOATCB_MASK (BBOCB_MASK &~ AT1_MASK) +#define BBOAT2CB_MASK (BBOCB_MASK &~ AT2_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) +#define BBOATBI_MASK (BBOAT2CB_MASK | BI_MASK) + +/* An Context form instruction. */ +#define CTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7)) +#define CTX_MASK CTX(0x3f, 0x7) + +/* An User Context form instruction. */ +#define UCTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x1f)) +#define UCTX_MASK UCTX(0x3f, 0x1f) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* A DE form instruction. */ +#define DEO(op, xop) (OP (op) | ((xop) & 0xf)) +#define DE_MASK DEO (0x3e, 0xf) + +/* An EVSEL form instruction. */ +#define EVSEL(op, xop) (OP (op) | (((unsigned long)(xop)) & 0xff) << 3) +#define EVSEL_MASK EVSEL(0x3f, 0xff) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | ((((unsigned long)(me)) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | ((((unsigned long)(sa)) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (((unsigned long)0x3ff) << 16) | (((unsigned long)1) << 1) | 1) + +/* An VX form instruction. */ +#define VX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7ff)) + +/* The mask for an VX form instruction. */ +#define VX_MASK VX(0x3f, 0x7ff) + +/* An VA form instruction. */ +#define VXA(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x03f)) + +/* The mask for an VA form instruction. */ +#define VXA_MASK VXA(0x3f, 0x3f) + +/* An VXR form instruction. */ +#define VXR(op, xop, rc) (OP (op) | (((rc) & 1) << 10) | (((unsigned long)(xop)) & 0x3ff)) + +/* The mask for a VXR form instruction. */ +#define VXR_MASK VXR(0x3f, 0x3ff, 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1)) + +/* A Z form instruction. */ +#define Z(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* A Z form instruction with the RC bit specified. */ +#define ZRC(op, xop, rc) (Z ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* The mask for a Z form instruction. */ +#define Z_MASK ZRC (0x3f, 0x1ff, 1) +#define Z2_MASK ZRC (0x3f, 0xff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An XRA_MASK with the W field clear. */ +#define XWRA_MASK (XRA_MASK & ~((unsigned long) 1 << 16)) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An XRT_MASK mask with the L bits clear. */ +#define XLRT_MASK (XRT_MASK & ~((unsigned long) 0x3 << 21)) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An XRARB_MASK, but with the L bit clear. */ +#define XRLARB_MASK (XRARB_MASK & ~((unsigned long) 1 << 16)) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An XRTRA_MASK, but with L bit clear. */ +#define XRTLRA_MASK (XRTRA_MASK & ~((unsigned long) 1 << 21)) + +/* An X form instruction with the L bit specified. */ +#define XOPL(op, xop, l) (X ((op), (xop)) | ((((unsigned long)(l)) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (((unsigned long)1) << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (((unsigned long)1) << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | ((((unsigned long)(to)) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An X form tlb instruction with the SH field specified. */ +#define XTLB(op, xop, sh) (X ((op), (xop)) | ((((unsigned long)(sh)) & 0x1f) << 11)) +#define XTLB_MASK (X_MASK | SH_MASK) + +/* An X form sync instruction. */ +#define XSYNC(op, xop, l) (X ((op), (xop)) | ((((unsigned long)(l)) & 3) << 21)) + +/* An X form sync instruction with everything filled in except the LS field. */ +#define XSYNC_MASK (0xff9fffff) + +/* An X_MASK, but with the EH bit clear. */ +#define XEH_MASK (X_MASK & ~((unsigned long )1)) + +/* An X form AltiVec dss instruction. */ +#define XDSS(op, xop, a) (X ((op), (xop)) | ((((unsigned long)(a)) & 1) << 25)) +#define XDSS_MASK XDSS(0x3f, 0x3ff, 1) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1) | (((unsigned long)(rc)) & 1)) +#define XFL_MASK XFL (0x3f, 0x3ff, 1) + +/* An X form isel instruction. */ +#define XISEL(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1)) +#define XISEL_MASK XISEL(0x3f, 0x1f) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | ((((unsigned long)(y)) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | ((((unsigned long)(cb)) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +#define BB_MASK (0x1f << 11) +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* A mask for branch instructions using the BH field. */ +#define XLBH_MASK (XL_MASK | (0x1c << 11)) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 1) | ((((unsigned long)(oe)) & 1) << 10) | (((unsigned long)(rc)) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 2) | (((unsigned long)(rc)) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (1 << 11) | (1 << 20)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm, p4) \ + (X ((op), (xop)) | ((((unsigned long)(fxm)) & 0xff) << 12) \ + | ((unsigned long)(p4) << 20)) + +#define SPR_MASK (0x3ff << 11) +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | ((((unsigned long)(spr)) & 0x1f) << 16) | ((((unsigned long)(spr)) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK & ~(0x1f << 16)) + +/* An X form instruction with everything filled in except the E field. */ +#define XE_MASK (0xffff7fff) + +/* An X form user context instruction. */ +#define XUC(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x1f)) +#define XUC_MASK XUC(0x3f, 0x1f) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) + +#define BOF (0x4) +#define BOFP (0x5) +#define BOFM4 (0x6) +#define BOFP4 (0x7) +#define BOT (0xc) +#define BOTP (0xd) +#define BOTM4 (0xe) +#define BOTP4 (0xf) + +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BODNZM4 (0x18) +#define BODNZP4 (0x19) +#define BODZM4 (0x1a) +#define BODZP4 (0x1b) + +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC +#define PPCCOM PPC_OPCODE_PPC | PPC_OPCODE_COMMON +#define PPC64 PPC_OPCODE_64 | PPC_OPCODE_PPC +#define COM PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON +#define COM32 PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_32 + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +static const struct powerpc_opcode powerpc_opcodes[] = { + +{ "cmplwi", OPL(10,0), OPL_MASK, PPCCOM, { OBF, RA, UI } }, +{ "cmpwi", OPL(11,0), OPL_MASK, PPCCOM, { OBF, RA, SI } }, +{ "cmpw", XOPL(31,0,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } }, +{ "cmplw", XOPL(31,32,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } }, +{ "fcmpu", X(63,0), X_MASK|(3<<21), COM, { BF, FRA, FRB } }, + +{ "li", OP(14), DRA_MASK, PPCCOM, { RT, SI } }, +{ "lis", OP(15), DRA_MASK, PPCCOM, { RT, SI } }, + +{ "addi", OP(14), OP_MASK, PPCCOM, { RT, RA0, SI } }, +{ "addis", OP(15), OP_MASK, PPCCOM, { RT,RA0,SI } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } }, +{ "bc", B(16,0,0), B_MASK, COM, { BO, BI, BD } }, +{ "bcl", B(16,0,1), B_MASK, COM, { BO, BI, BD } }, +{ "b", B(18,0,0), B_MASK, COM, { LI } }, +{ "bl", B(18,0,1), B_MASK, COM, { LI } }, +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, COM, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, COM, { 0 } }, + +{ "rlwinm", M(21,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "nop", OP(24), 0xffffffff, PPCCOM, { 0 } }, +{ "ori", OP(24), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "xoris", OP(27), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "ldx", X(31,21), X_MASK, PPC64, { RT, RA0, RB } }, +{ "lwzx", X(31,23), X_MASK, PPCCOM, { RT, RA0, RB } }, +{ "slw", XRC(31,24,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "and", XRC(31,28,0), X_MASK, COM, { RA, RS, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "lbzx", X(31,87), X_MASK, COM, { RT, RA0, RB } }, +{ "neg", XO(31,104,0,0), XORB_MASK, COM, { RT, RA } }, +{ "not", XRC(31,124,0), X_MASK, COM, { RA, RS, RBS } }, +{ "stwx", X(31,151), X_MASK, PPCCOM, { RS, RA0, RB } }, +{ "stbx", X(31,215), X_MASK, COM, { RS, RA0, RB } }, +{ "mullw", XO(31,235,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "add", XO(31,266,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "lhzx", X(31,279), X_MASK, COM, { RT, RA0, RB } }, +{ "xor", XRC(31,316,0), X_MASK, COM, { RA, RS, RB } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, COM, { RT } }, +{ "sthx", X(31,407), X_MASK, COM, { RS, RA0, RB } }, +{ "mr", XRC(31,444,0), X_MASK, COM, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, COM, { RA, RS, RB } }, +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, COM, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, COM, { RS } }, +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "lfsx", X(31,535), X_MASK, COM, { FRT, RA0, RB } }, +{ "srw", XRC(31,536,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "stfsx", X(31,663), X_MASK, COM, { FRS, RA0, RB } }, +{ "sraw", XRC(31,792,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "extsh", XRC(31,922,0), XRB_MASK, PPCCOM, { RA, RS } }, +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, + +{ "lwz", OP(32), OP_MASK, PPCCOM, { RT, D, RA0 } }, +{ "lbz", OP(34), OP_MASK, COM, { RT, D, RA0 } }, +{ "stw", OP(36), OP_MASK, PPCCOM, { RS, D, RA0 } }, +{ "stwu", OP(37), OP_MASK, PPCCOM, { RS, D, RAS } }, +{ "stb", OP(38), OP_MASK, COM, { RS, D, RA0 } }, +{ "lhz", OP(40), OP_MASK, COM, { RT, D, RA0 } }, +{ "sth", OP(44), OP_MASK, COM, { RS, D, RA0 } }, +{ "lfs", OP(48), OP_MASK, COM, { FRT, D, RA0 } }, +{ "lfd", OP(50), OP_MASK, COM, { FRT, D, RA0 } }, +{ "stfs", OP(52), OP_MASK, COM, { FRS, D, RA0 } }, +{ "stfd", OP(54), OP_MASK, COM, { FRS, D, RA0 } }, +{ "ld", DSO(58,0), DS_MASK, PPC64, { RT, DS, RA0 } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "std", DSO(62,0), DS_MASK, PPC64, { RS, DS, RA0 } }, +{ "stdu", DSO(62,1), DS_MASK, PPC64, { RS, DS, RAS } }, +{ "frsp", XRC(63,12,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fsub", A(63,20,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fneg", XRC(63,40,0), XRA_MASK, COM, { FRT, FRB } }, +}; + +static const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); diff --git a/src/qcommon/vm_powerpc_asm.h b/src/qcommon/vm_powerpc_asm.h new file mode 100644 index 00000000..9bccf18a --- /dev/null +++ b/src/qcommon/vm_powerpc_asm.h @@ -0,0 +1,156 @@ +/* +=========================================================================== +Copyright (C) 2008 Przemyslaw Iskra + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifndef VM_POWERPC_ASM_H +#define VM_POWERPC_ASM_H + +/* + * Register information according to: + * http://refspecs.freestandards.org/elf/elfspec_ppc.pdf + */ + +#define r0 0 // volatile +#define r1 1 // caller safe ( stack pointer ) +#define r2 2 // reserved +#define r3 3 // callee safe +#define r4 4 // callee safe +#define r5 5 // callee safe +#define r6 6 // callee safe +#define r7 7 // callee safe +#define r8 8 // callee safe +#define r9 9 // callee safe +#define r10 10 // callee safe +#define r11 11 // volatile +#define r12 12 // volatile +#define r13 13 // reserved ( small data area ) +#define r14 14 // caller safe +#define r15 15 // caller safe +#define r16 16 // caller safe +#define r17 17 // caller safe +#define r18 18 // caller safe +#define r19 19 // caller safe +#define r20 20 // caller safe +#define r21 21 // caller safe +#define r22 22 // caller safe +#define r23 23 // caller safe +#define r24 24 // caller safe +#define r25 25 // caller safe +#define r26 26 // caller safe +#define r27 27 // caller safe +#define r28 28 // caller safe +#define r29 29 // caller safe +#define r30 30 // caller safe +#define r31 31 // caller safe ( environment pointers ) + +#define f0 0 // callee safe +#define f1 1 // callee safe +#define f2 2 // callee safe +#define f3 3 // callee safe +#define f4 4 // callee safe +#define f5 5 // callee safe +#define f6 6 // callee safe +#define f7 7 // callee safe +#define f8 8 // callee safe +#define f9 9 // callee safe +#define f10 10 // callee safe +#define f11 11 // callee safe +#define f12 12 // callee safe +#define f13 13 // callee safe +#define f14 14 // caller safe +#define f15 15 // caller safe +#define f16 16 // caller safe +#define f17 17 // caller safe +#define f18 18 // caller safe +#define f19 19 // caller safe +#define f20 20 // caller safe +#define f21 21 // caller safe +#define f22 22 // caller safe +#define f23 23 // caller safe +#define f24 24 // caller safe +#define f25 25 // caller safe +#define f26 26 // caller safe +#define f27 27 // caller safe +#define f28 28 // caller safe +#define f29 29 // caller safe +#define f30 30 // caller safe +#define f31 31 // caller safe + +#define cr0 0 // volatile +#define cr1 1 // volatile +#define cr2 2 // caller safe +#define cr3 3 // caller safe +#define cr4 4 // caller safe +#define cr5 5 // volatile +#define cr6 6 // volatile +#define cr7 7 // volatile + +#define lt 0 +#define gt 1 +#define eq 2 +#define so 3 + +// branch bo field values +#define branchLikely 1 +#define branchFalse 4 +#define branchTrue 12 +#define branchAlways 20 + +// branch extensions (change branch type) +#define branchExtLink 0x0001 + + +/* + * This list must match exactly the powerpc_opcodes list from vm_powerpc_asm.c + * If you're changing the original list remember to regenerate this one. You + * may do so using this perl script: + perl -p -e 'BEGIN{%t=("-"=>m=>"+"=>p=>"."=>d=>);$l=""}$o=0 if/^}/; + if($o && s/^{ "(.*?)([\.+-])?".+/i\U$1\E$t{$2}/s){$_.="_" while$l{$_}; + $l{$_}=1;if(length $l.$_ > 70){$s=$_;$_="\t$l\n";$l="$s,"}else + {$l.=" $_,";$_=undef}}else{$o=1 if/powerpc_opcodes.*=/;$_=undef}; + END{print "\t$l\n"}' < vm_powerpc_asm.c + */ + +typedef enum powerpc_iname { + iCMPLWI, iCMPWI, iCMPW, iCMPLW, iFCMPU, iLI, iLIS, iADDI, iADDIS, + iBLTm, iBC, iBCL, iB, iBL, iBLR, iBCTR, iBCTRL, iRLWINM, iNOP, iORI, + iXORIS, iLDX, iLWZX, iSLW, iAND, iSUB, iLBZX, iNEG, iNOT, iSTWX, iSTBX, + iMULLW, iADD, iLHZX, iXOR, iMFLR, iSTHX, iMR, iOR, iDIVWU, iMTLR, + iMTCTR, iDIVW, iLFSX, iSRW, iSTFSX, iSRAW, iEXTSH, iEXTSB, iLWZ, iLBZ, + iSTW, iSTWU, iSTB, iLHZ, iSTH, iLFS, iLFD, iSTFS, iSTFD, iLD, iFDIVS, + iFSUBS, iFADDS, iFMULS, iSTD, iSTDU, iFRSP, iFCTIWZ, iFSUB, iFNEG, +} powerpc_iname_t; + +#include + +typedef uint32_t ppc_instruction_t; + +extern ppc_instruction_t +asm_instruction( powerpc_iname_t, const int, const long int * ); + +#define IN( inst, args... ) \ +({\ + const long int argv[] = { args };\ + const int argc = sizeof( argv ) / sizeof( argv[0] ); \ + asm_instruction( inst, argc, argv );\ +}) + +#endif diff --git a/src/renderer/tr_bsp.c b/src/renderer/tr_bsp.c index fd1b64b6..83633c00 100644 --- a/src/renderer/tr_bsp.c +++ b/src/renderer/tr_bsp.c @@ -1796,7 +1796,10 @@ Called directly from cgame void RE_LoadWorldMap( const char *name ) { int i; dheader_t *header; - byte *buffer; + union { + byte *b; + void *v; + } buffer; byte *startMarker; if ( tr.worldMapLoaded ) { @@ -1814,8 +1817,8 @@ void RE_LoadWorldMap( const char *name ) { tr.worldMapLoaded = qtrue; // load it - ri.FS_ReadFile( name, (void **)&buffer ); - if ( !buffer ) { + ri.FS_ReadFile( name, &buffer.v ); + if ( !buffer.b ) { ri.Error (ERR_DROP, "RE_LoadWorldMap: %s not found", name); } @@ -1832,7 +1835,7 @@ void RE_LoadWorldMap( const char *name ) { startMarker = ri.Hunk_Alloc(0, h_low); c_gridVerts = 0; - header = (dheader_t *)buffer; + header = (dheader_t *)buffer.b; fileBase = (byte *)header; i = LittleLong (header->version); @@ -1864,6 +1867,6 @@ void RE_LoadWorldMap( const char *name ) { // only set tr.world now that we know the entire level has loaded properly tr.world = &s_worldData; - ri.FS_FreeFile( buffer ); + ri.FS_FreeFile( buffer.v ); } diff --git a/src/renderer/tr_image.c b/src/renderer/tr_image.c index 836433b5..5ed98450 100644 --- a/src/renderer/tr_image.c +++ b/src/renderer/tr_image.c @@ -1432,7 +1432,11 @@ qhandle_t RE_RegisterSkin( const char *name ) { qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; - char *text, *text_p; + union { + char *c; + void *v; + } text; + char *text_p; char *token; char surfName[MAX_QPATH]; @@ -1481,12 +1485,12 @@ qhandle_t RE_RegisterSkin( const char *name ) { } // load and parse the skin file - ri.FS_ReadFile( name, (void **)&text ); - if ( !text ) { + ri.FS_ReadFile( name, &text.v ); + if ( !text.c ) { return 0; } - text_p = text; + text_p = text.c; while ( text_p && *text_p ) { // get surface name token = CommaParse( &text_p ); @@ -1515,7 +1519,7 @@ qhandle_t RE_RegisterSkin( const char *name ) { skin->numSurfaces++; } - ri.FS_FreeFile( text ); + ri.FS_FreeFile( text.v ); // never let a skin have 0 shaders diff --git a/src/renderer/tr_image_bmp.c b/src/renderer/tr_image_bmp.c index 190b878f..0b78abb8 100644 --- a/src/renderer/tr_image_bmp.c +++ b/src/renderer/tr_image_bmp.c @@ -50,7 +50,10 @@ void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) int row, column; byte *buf_p; byte *end; - byte *buffer = NULL; + union { + byte *b; + void *v; + } buffer; int length; BMPHeader_t bmpHeader; byte *bmpRGBA; @@ -66,8 +69,8 @@ void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) // // load the file // - length = ri.FS_ReadFile( ( char * ) name, (void **)&buffer); - if (!buffer || length < 0) { + length = ri.FS_ReadFile( ( char * ) name, &buffer.v); + if (!buffer.b || length < 0) { return; } @@ -76,8 +79,8 @@ void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) ri.Error( ERR_DROP, "LoadBMP: header too short (%s)\n", name ); } - buf_p = buffer; - end = buffer + length; + buf_p = buffer.b; + end = buffer.b + length; bmpHeader.id[0] = *buf_p++; bmpHeader.id[1] = *buf_p++; @@ -119,12 +122,12 @@ void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) buf_p += sizeof(bmpHeader.palette); } - if (buffer + bmpHeader.bitmapDataOffset > end) + if (buffer.b + bmpHeader.bitmapDataOffset > end) { ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)\n", name ); } - buf_p = buffer + bmpHeader.bitmapDataOffset; + buf_p = buffer.b + bmpHeader.bitmapDataOffset; if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) { @@ -231,6 +234,6 @@ void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) } } - ri.FS_FreeFile( buffer ); + ri.FS_FreeFile( buffer.v ); } diff --git a/src/renderer/tr_image_jpg.c b/src/renderer/tr_image_jpg.c index fb4f2121..2e1a52a7 100644 --- a/src/renderer/tr_image_jpg.c +++ b/src/renderer/tr_image_jpg.c @@ -57,7 +57,10 @@ void R_LoadJPG( const char *filename, unsigned char **pic, int *width, int *heig unsigned pixelcount, memcount; unsigned char *out; int len; - byte *fbuffer; + union { + byte *b; + void *v; + } fbuffer; byte *buf; /* In this example we want to open the input file before doing anything else, @@ -66,8 +69,8 @@ void R_LoadJPG( const char *filename, unsigned char **pic, int *width, int *heig * requires it in order to read binary files. */ - len = ri.FS_ReadFile ( ( char * ) filename, (void **)&fbuffer); - if (!fbuffer || len < 0) { + len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v); + if (!fbuffer.b || len < 0) { return; } @@ -85,7 +88,7 @@ void R_LoadJPG( const char *filename, unsigned char **pic, int *width, int *heig /* Step 2: specify data source (eg, a file) */ - jpeg_mem_src(&cinfo, fbuffer, len); + jpeg_mem_src(&cinfo, fbuffer.b, len); /* Step 3: read file parameters with jpeg_read_header() */ @@ -203,7 +206,7 @@ void R_LoadJPG( const char *filename, unsigned char **pic, int *width, int *heig * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ - ri.FS_FreeFile (fbuffer); + ri.FS_FreeFile (fbuffer.v); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.pub.num_warnings is nonzero). diff --git a/src/renderer/tr_image_pcx.c b/src/renderer/tr_image_pcx.c index db407dea..d1b1a5ee 100644 --- a/src/renderer/tr_image_pcx.c +++ b/src/renderer/tr_image_pcx.c @@ -50,7 +50,10 @@ typedef struct { void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) { - byte *raw; + union { + byte *b; + void *v; + } raw; byte *end; pcx_t *pcx; int len; @@ -71,23 +74,23 @@ void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) // // load the file // - len = ri.FS_ReadFile( ( char * ) filename, (void **)&raw); - if (!raw || len < 0) { + len = ri.FS_ReadFile( ( char * ) filename, &raw.v); + if (!raw.b || len < 0) { return; } if((unsigned)len < sizeof(pcx_t)) { ri.Printf (PRINT_ALL, "PCX truncated: %s\n", filename); - ri.FS_FreeFile (raw); + ri.FS_FreeFile (raw.v); return; } // // parse the PCX file // - pcx = (pcx_t *)raw; - end = raw+len; + pcx = (pcx_t *)raw.b; + end = raw.b+len; w = LittleShort(pcx->xmax)+1; h = LittleShort(pcx->ymax)+1; @@ -107,7 +110,7 @@ void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) pix = pic8 = ri.Malloc ( size ); - raw = pcx->data; + raw.b = pcx->data; // FIXME: should use bytes_per_line but original q3 didn't do that either while(pix < pic8+size) { @@ -117,16 +120,16 @@ void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) continue; } - if(raw+1 > end) + if(raw.b+1 > end) break; - dataByte = *raw++; + dataByte = *raw.b++; if((dataByte & 0xC0) == 0xC0) { - if(raw+1 > end) + if(raw.b+1 > end) break; runLength = dataByte & 0x3F; - dataByte = *raw++; + dataByte = *raw.b++; } else runLength = 1; @@ -139,7 +142,7 @@ void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) ri.Free (pic8); } - if (raw-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c) + if (raw.b-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c) { ri.Printf (PRINT_ALL, "PCX missing palette: %s\n", filename); ri.FS_FreeFile (pcx); diff --git a/src/renderer/tr_image_png.c b/src/renderer/tr_image_png.c index e33ffffa..ef44c165 100644 --- a/src/renderer/tr_image_png.c +++ b/src/renderer/tr_image_png.c @@ -215,6 +215,10 @@ struct BufferedFile static struct BufferedFile *ReadBufferedFile(const char *name) { struct BufferedFile *BF; + union { + byte *b; + void *v; + } buffer; /* * input verification @@ -248,7 +252,8 @@ static struct BufferedFile *ReadBufferedFile(const char *name) * Read the file. */ - BF->Length = ri.FS_ReadFile((char *) name, (void **) &BF->Buffer); + BF->Length = ri.FS_ReadFile((char *) name, &buffer.v); + BF->Buffer = buffer.b; /* * Did we get it? Is it big enough? diff --git a/src/renderer/tr_image_tga.c b/src/renderer/tr_image_tga.c index 2147acdc..e66e60de 100644 --- a/src/renderer/tr_image_tga.c +++ b/src/renderer/tr_image_tga.c @@ -45,7 +45,10 @@ void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) int row, column; byte *buf_p; byte *end; - byte *buffer = NULL; + union { + byte *b; + void *v; + } buffer; TargaHeader targa_header; byte *targa_rgba; int length; @@ -60,8 +63,8 @@ void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) // // load the file // - length = ri.FS_ReadFile ( ( char * ) name, (void **)&buffer); - if (!buffer || length < 0) { + length = ri.FS_ReadFile ( ( char * ) name, &buffer.v); + if (!buffer.b || length < 0) { return; } @@ -70,8 +73,8 @@ void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) ri.Error( ERR_DROP, "LoadTGA: header too short (%s)\n", name ); } - buf_p = buffer; - end = buffer + length; + buf_p = buffer.b; + end = buffer.b + length; targa_header.id_length = buf_p[0]; targa_header.colormap_type = buf_p[1]; @@ -313,5 +316,5 @@ void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) *pic = targa_rgba; - ri.FS_FreeFile (buffer); + ri.FS_FreeFile (buffer.v); } diff --git a/src/renderer/tr_model.c b/src/renderer/tr_model.c index b4ca744f..8707d8a7 100644 --- a/src/renderer/tr_model.c +++ b/src/renderer/tr_model.c @@ -84,7 +84,10 @@ asked for again. */ qhandle_t RE_RegisterModel( const char *name ) { model_t *mod; - unsigned *buf; + union { + unsigned *u; + void *v; + } buf; int lod; int ident; qboolean loaded = qfalse; @@ -152,19 +155,19 @@ qhandle_t RE_RegisterModel( const char *name ) { { int filesize; - filesize = ri.FS_ReadFile(name, (void **) &buf); - if(!buf) + filesize = ri.FS_ReadFile(name, (void **) &buf.v); + if(!buf.u) { ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name); mod->type = MOD_BAD; return 0; } - ident = LittleLong(*(unsigned *)buf); + ident = LittleLong(*(unsigned *)buf.u); if(ident == MDR_IDENT) - loaded = R_LoadMDR(mod, buf, filesize, name); + loaded = R_LoadMDR(mod, buf.u, filesize, name); - ri.FS_FreeFile (buf); + ri.FS_FreeFile (buf.v); if(!loaded) { @@ -185,26 +188,26 @@ qhandle_t RE_RegisterModel( const char *name ) { else Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext); - ri.FS_ReadFile( namebuf, (void **)&buf ); - if ( !buf ) { + ri.FS_ReadFile( namebuf, &buf.v ); + if ( !buf.u ) { continue; } loadmodel = mod; - ident = LittleLong(*(unsigned *)buf); + ident = LittleLong(*(unsigned *)buf.u); if ( ident == MD4_IDENT ) { - loaded = R_LoadMD4( mod, buf, name ); + loaded = R_LoadMD4( mod, buf.u, name ); } else { if ( ident != MD3_IDENT ) { ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", name); goto fail; } - loaded = R_LoadMD3( mod, lod, buf, name ); + loaded = R_LoadMD3( mod, lod, buf.u, name ); } - ri.FS_FreeFile (buf); + ri.FS_FreeFile (buf.v); if ( !loaded ) { if ( lod == 0 ) { diff --git a/src/sdl/sdl_input.c b/src/sdl/sdl_input.c index 98f8525a..3263932a 100644 --- a/src/sdl/sdl_input.c +++ b/src/sdl/sdl_input.c @@ -84,7 +84,7 @@ static void IN_PrintKey( const SDL_keysym *keysym, keyNum_t key, qboolean down ) else Com_Printf( " " ); - Com_Printf( "0x%hx \"%s\"", keysym->scancode, + Com_Printf( "0x%02x \"%s\"", keysym->scancode, SDL_GetKeyName( keysym->sym ) ); if( keysym->mod & KMOD_LSHIFT ) Com_Printf( " KMOD_LSHIFT" ); @@ -100,11 +100,11 @@ static void IN_PrintKey( const SDL_keysym *keysym, keyNum_t key, qboolean down ) if( keysym->mod & KMOD_MODE ) Com_Printf( " KMOD_MODE" ); if( keysym->mod & KMOD_RESERVED ) Com_Printf( " KMOD_RESERVED" ); - Com_Printf( " Q:%d(%s)", key, Key_KeynumToString( key ) ); + Com_Printf( " Q:0x%02x(%s)", key, Key_KeynumToString( key ) ); if( keysym->unicode ) { - Com_Printf( " U:%d", keysym->unicode ); + Com_Printf( " U:0x%02x", keysym->unicode ); if( keysym->unicode > ' ' && keysym->unicode < '~' ) Com_Printf( "(%c)", (char)keysym->unicode ); @@ -120,7 +120,7 @@ static void IN_PrintKey( const SDL_keysym *keysym, keyNum_t key, qboolean down ) IN_IsConsoleKey =============== */ -static qboolean IN_IsConsoleKey( keyNum_t key, const char character ) +static qboolean IN_IsConsoleKey( keyNum_t key, const unsigned char character ) { typedef struct consoleKey_s { @@ -133,7 +133,7 @@ static qboolean IN_IsConsoleKey( keyNum_t key, const char character ) union { keyNum_t key; - char character; + unsigned char character; } u; } consoleKey_t; @@ -153,34 +153,36 @@ static qboolean IN_IsConsoleKey( keyNum_t key, const char character ) while( numConsoleKeys < MAX_CONSOLE_KEYS ) { consoleKey_t *c = &consoleKeys[ numConsoleKeys ]; - char *keyName; + int charCode = 0; token = COM_Parse( &text_p ); if( !token[ 0 ] ) break; - c->u.key = Key_StringToKeynum( token ); + if( strlen( token ) == 4 ) + charCode = Com_HexStrToInt( token ); - // 0 isn't a key - if( c->u.key == 0 ) - continue; - - keyName = Key_KeynumToString( c->u.key ); - - if( strlen( keyName ) == 1 ) + if( charCode > 0 ) { c->type = CHARACTER; - c->u.character = *keyName; + c->u.character = (unsigned char)charCode; } else + { c->type = KEY; + c->u.key = Key_StringToKeynum( token ); + + // 0 isn't a key + if( c->u.key <= 0 ) + continue; + } numConsoleKeys++; } } - // Use the character in preference to the key name - if( character != '\0' ) + // If the character is the same as the key, prefer the character + if( key == character ) key = 0; for( i = 0; i < numConsoleKeys; i++ ) @@ -212,7 +214,7 @@ IN_TranslateSDLToQ3Key static const char *IN_TranslateSDLToQ3Key( SDL_keysym *keysym, keyNum_t *key, qboolean down ) { - static char buf[ 2 ] = { '\0', '\0' }; + static unsigned char buf[ 2 ] = { '\0', '\0' }; *buf = '\0'; *key = 0; @@ -311,9 +313,9 @@ static const char *IN_TranslateSDLToQ3Key( SDL_keysym *keysym, } } - if( down && keysym->unicode && !( keysym->unicode & 0xFF80 ) ) + if( down && keysym->unicode && !( keysym->unicode & 0xFF00 ) ) { - char ch = (char)keysym->unicode & 0x7F; + unsigned char ch = (unsigned char)keysym->unicode & 0xFF; switch( ch ) { @@ -333,6 +335,17 @@ static const char *IN_TranslateSDLToQ3Key( SDL_keysym *keysym, if( in_keyboardDebug->integer ) IN_PrintKey( keysym, *key, down ); + // Keys that have ASCII names but produce no character are probably + // dead keys -- ignore them + if( down && strlen( Key_KeynumToString( *key ) ) == 1 && + keysym->unicode == 0 ) + { + if( in_keyboardDebug->integer ) + Com_Printf( " Ignored dead key '%c'\n", *key ); + + *key = 0; + } + if( IN_IsConsoleKey( *key, *buf ) ) { // Console keys can't be bound or generate characters @@ -340,7 +353,11 @@ static const char *IN_TranslateSDLToQ3Key( SDL_keysym *keysym, *buf = '\0'; } - return buf; + // Don't allow extended ASCII to generate characters + if( *buf & 0x80 ) + *buf = '\0'; + + return (char *)buf; } #ifdef MACOS_X_ACCELERATION_HACK @@ -865,7 +882,7 @@ IN_ProcessEvents static void IN_ProcessEvents( void ) { SDL_Event e; - const char *p = NULL; + const char *character = NULL; keyNum_t key = 0; if( !SDL_WasInit( SDL_INIT_VIDEO ) ) @@ -888,20 +905,19 @@ static void IN_ProcessEvents( void ) switch( e.type ) { case SDL_KEYDOWN: - p = IN_TranslateSDLToQ3Key( &e.key.keysym, &key, qtrue ); + character = IN_TranslateSDLToQ3Key( &e.key.keysym, &key, qtrue ); if( key ) Com_QueueEvent( 0, SE_KEY, key, qtrue, 0, NULL ); - if( p ) - { - while( *p ) - Com_QueueEvent( 0, SE_CHAR, *p++, 0, 0, NULL ); - } + if( character ) + Com_QueueEvent( 0, SE_CHAR, *character, 0, 0, NULL ); break; case SDL_KEYUP: IN_TranslateSDLToQ3Key( &e.key.keysym, &key, qfalse ); - Com_QueueEvent( 0, SE_KEY, key, qfalse, 0, NULL ); + + if( key ) + Com_QueueEvent( 0, SE_KEY, key, qfalse, 0, NULL ); break; case SDL_MOUSEMOTION: diff --git a/src/server/sv_ccmds.c b/src/server/sv_ccmds.c index 367ca23d..1da5aaeb 100644 --- a/src/server/sv_ccmds.c +++ b/src/server/sv_ccmds.c @@ -257,6 +257,17 @@ static void SV_KillServer_f( void ) { //=========================================================== +/* +================== +SV_CompleteMapName +================== +*/ +static void SV_CompleteMapName( char *args, int argNum ) { + if( argNum == 2 ) { + Field_CompleteFilename( "maps", "bsp", qtrue ); + } +} + /* ================== SV_AddOperatorCommands @@ -276,7 +287,9 @@ void SV_AddOperatorCommands( void ) { Cmd_AddCommand ("map_restart", SV_MapRestart_f); Cmd_AddCommand ("sectorlist", SV_SectorList_f); Cmd_AddCommand ("map", SV_Map_f); + Cmd_SetCommandCompletionFunc( "map", SV_CompleteMapName ); Cmd_AddCommand ("devmap", SV_Map_f); + Cmd_SetCommandCompletionFunc( "devmap", SV_CompleteMapName ); Cmd_AddCommand ("killserver", SV_KillServer_f); } diff --git a/src/server/sv_client.c b/src/server/sv_client.c index 4fa0b24f..4b7b3bb6 100644 --- a/src/server/sv_client.c +++ b/src/server/sv_client.c @@ -1251,6 +1251,7 @@ void SV_ExecuteClientCommand( client_t *cl, const char *s, qboolean clientOK ) { if (clientOK) { // pass unknown strings to the game if (!u->name && sv.state == SS_GAME) { + Cmd_Args_Sanitize(); VM_Call( gvm, GAME_CLIENT_COMMAND, cl - svs.clients ); } } diff --git a/src/server/sv_game.c b/src/server/sv_game.c index df13500d..d375c095 100644 --- a/src/server/sv_game.c +++ b/src/server/sv_game.c @@ -285,14 +285,9 @@ void SV_GetUsercmd( int clientNum, usercmd_t *cmd ) { //============================================== static int FloatAsInt( float f ) { - union - { - int i; - float f; - } temp; - - temp.f = f; - return temp.i; + floatint_t fi; + fi.f = f; + return fi.i; } /* diff --git a/src/server/sv_main.c b/src/server/sv_main.c index 7b66aa6f..01da0f5d 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -225,6 +225,7 @@ but not on every player enter or exit. void SV_MasterHeartbeat( void ) { static netadr_t adr[MAX_MASTER_SERVERS]; int i; + int res; // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { @@ -251,11 +252,13 @@ void SV_MasterHeartbeat( void ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); - if ( !NET_StringToAdr( sv_master[i]->string, &adr[i], NA_UNSPEC ) ) { + res = NET_StringToAdr( sv_master[i]->string, &adr[i], NA_UNSPEC ); + if ( !res ) { Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); continue; } - if ( !strchr( sv_master[i]->string, ':' ) ) { + if ( res == 2 ) { + // if no port was specified, use the default master port adr[i].port = BigShort( PORT_MASTER ); } Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i])); diff --git a/src/sys/con_tty.c b/src/sys/con_tty.c index 687d1dd4..cdd686a1 100644 --- a/src/sys/con_tty.c +++ b/src/sys/con_tty.c @@ -87,12 +87,14 @@ send "\b \b" static void CON_Back( void ) { char key; + size_t size; + key = '\b'; - write(1, &key, 1); + size = write(1, &key, 1); key = ' '; - write(1, &key, 1); + size = write(1, &key, 1); key = '\b'; - write(1, &key, 1); + size = write(1, &key, 1); } /* @@ -143,12 +145,13 @@ static void CON_Show( void ) ttycon_hide--; if (ttycon_hide == 0) { - write( 1, "]", 1 ); + size_t size; + size = write( 1, "]", 1 ); if (TTY_con.cursor) { for (i=0; ix.name = stringf("%U", p->u.c.v.p); break; case F: { // JDC: added this to get inline floats - unsigned temp; + floatint_t temp; - *(float *)&temp = p->u.c.v.d; - p->x.name = stringf("%U", temp ); + temp.f = p->u.c.v.d; + p->x.name = stringf("%U", temp.ui ); } break;// JDC: added this default: assert(0); @@ -260,7 +261,8 @@ static void LoadSourceFile( const char *filename ) { length = filelength( f ); sourceFile = malloc( length + 1 ); if ( sourceFile ) { - fread( sourceFile, length, 1, f ); + size_t size; + size = fread( sourceFile, length, 1, f ); sourceFile[length] = 0; } diff --git a/src/tools/lcc/src/c.h b/src/tools/lcc/src/c.h index e36380ef..68c8f629 100644 --- a/src/tools/lcc/src/c.h +++ b/src/tools/lcc/src/c.h @@ -98,6 +98,12 @@ typedef struct { void *xt; } Xtype; +typedef union { + float f; + int i; + unsigned int ui; +} floatint_t; + #include "config.h" typedef struct metrics { unsigned char size, align, outofline; -- cgit