mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-10 07:12:07 +00:00
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:
parent
e1ec177dbe
commit
ce2c3292c6
4 changed files with 30 additions and 0 deletions
|
@ -154,3 +154,26 @@ const char *Sys_GetBinaryDir(void)
|
|||
|
||||
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
|
||||
|
|
|
@ -52,6 +52,9 @@ main(int argc, char **argv)
|
|||
/* register signal handler */
|
||||
registerHandler();
|
||||
|
||||
/* Setup FPU if necessary */
|
||||
Sys_SetupFPU();
|
||||
|
||||
/* Are we portable? */
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-portable") == 0) {
|
||||
|
|
|
@ -785,6 +785,9 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|||
/* Make the current instance global */
|
||||
global_hInstance = hInstance;
|
||||
|
||||
/* Setup FPU if necessary */
|
||||
Sys_SetupFPU();
|
||||
|
||||
/* Force DPI awareness */
|
||||
Sys_SetHighDPIMode();
|
||||
|
||||
|
|
|
@ -773,6 +773,7 @@ void Sys_FreeLibrary(void *handle);
|
|||
void *Sys_LoadLibrary(const char *path, const char *sym, void **handle);
|
||||
void *Sys_GetProcAddress(void *handle, const char *sym);
|
||||
void Sys_RedirectStdout(void);
|
||||
void Sys_SetupFPU(void);
|
||||
|
||||
/* CLIENT / SERVER SYSTEMS */
|
||||
|
||||
|
|
Loading…
Reference in a new issue