Force the x87 FPU to double precision mode.

The original client used single precision mode on Windows and the
default mode on all other platforms. Most platform (at least OS X,
FreeBSD, NetBSD up to 6.0, OpenBSD and Solaris) set double precision
as default, Linux sets extended double precision... When playing a
network game there're several possibilities:

* Same precision on both sides: This one is okay, of course.
* single precision <-> double precision: This one is okay, too. I guess
  this is because the code allows a small deviation between client and
  server to work around imprecisions introduced be the network protocol.
* double precision <-> extended double precision: This one is okay,
  likely for the same reasons given above.
* single precision <-> extended double precision: This one gives a lot
  of misspredictions at client side.

All of these are more or less academic these days. Yamagi Quake II used
the platforms default mode for ages. And both gcc and clang default to
SSE2 math (with double precision as default on all platforms) when
compiling for amd64. So the only reasonable case is Linux/i386 on one
side and the original client or another source port on Windows/i386 at
the other side.

Work around this by forcing the x87 to double precision mode.
This commit is contained in:
Yamagi Burmeister 2017-09-20 21:55:43 +02:00
parent e1ec177dbe
commit ce2c3292c6
4 changed files with 30 additions and 0 deletions

View file

@ -154,3 +154,26 @@ const char *Sys_GetBinaryDir(void)
return exeDir; return exeDir;
} }
#if defined (__GNUC__) && (__i386 || __x86_64__)
void Sys_SetupFPU(void) {
// Get current x87 control word
volatile unsigned short old_cw = 0;
asm ("fstcw %0" : : "m" (*&old_cw));
unsigned short new_cw = old_cw;
// The precision is set through bit 8 and 9. For
// double precision bit 8 must unset and bit 9 set.
new_cw &= ~(1 << 8);
new_cw |= (1 << 9);
// Setting the control word is expensive since it
// resets the FPU state. Do it only if necessary.
if (new_cw != old_cw) {
asm ("fldcw %0" : : "m" (*&new_cw));
}
}
#else
void Sys_SetupFPU(void) {
}
#endif

View file

@ -52,6 +52,9 @@ main(int argc, char **argv)
/* register signal handler */ /* register signal handler */
registerHandler(); registerHandler();
/* Setup FPU if necessary */
Sys_SetupFPU();
/* Are we portable? */ /* Are we portable? */
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "-portable") == 0) { if (strcmp(argv[i], "-portable") == 0) {

View file

@ -785,6 +785,9 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
/* Make the current instance global */ /* Make the current instance global */
global_hInstance = hInstance; global_hInstance = hInstance;
/* Setup FPU if necessary */
Sys_SetupFPU();
/* Force DPI awareness */ /* Force DPI awareness */
Sys_SetHighDPIMode(); Sys_SetHighDPIMode();

View file

@ -773,6 +773,7 @@ void Sys_FreeLibrary(void *handle);
void *Sys_LoadLibrary(const char *path, const char *sym, void **handle); void *Sys_LoadLibrary(const char *path, const char *sym, void **handle);
void *Sys_GetProcAddress(void *handle, const char *sym); void *Sys_GetProcAddress(void *handle, const char *sym);
void Sys_RedirectStdout(void); void Sys_RedirectStdout(void);
void Sys_SetupFPU(void);
/* CLIENT / SERVER SYSTEMS */ /* CLIENT / SERVER SYSTEMS */