Blog

Basilisk II in... The Longest Double

17 April 2020

Basilisk II is an emulator for old Macintosh computers with 680x0 CPUs (particularly 68040). It is not the most stable of emulators, but it does have some advantages, the most significant being FPU support. This is useful for games. There are actually three FPU implementations that can be used, but the default choice is probably the fastest. But... is it the buggiest? Or is there a problem on my end?

I am building Basilisk II with MinGW (the original fork) on Windows. It also supports Linux and Mac OS X, but these problems are only on Windows builds.

The Problem

The best program that shows the problem is the Mac version of Wolfenstein 3D. I think it is a much nicer port of the game, with higher resolution graphics, sounds, and has more upgrades. I suppose it makes it an easier game overall, but I need all the help I can get. Anyway, observe:

Screenshot of Wolfenstein 3D with graphical glitches.

Oh dear. The right half of the game area is corrupted, and the textures and details are missing. If you turn to the right, those details spring into view, but more artifacts appear. If you try to play the game and ignore the issue, Basilisk II itself eventually crashes. This problem doesn't happen in Linux. What is causing this?

Long Double

When compiling, I noticed that there were a few overflow warnings where long numbers would not fit into a double. It turned out the warnings came from numeric literals that were missing the 'L' suffix. Before I worked that out, I ended up looking at the file "fpu/types.h". This header works out if the longer floating point data types are available, or makes substitutions if not.

According to sizeof(long double), and also this file, long doubles are 96-bit (12 bytes) long. However, I heard that long doubles on Windows are equal to double, at least with Microsoft Visual C - so, 64-bit. I forced the use of 64-bit doubles, and... no more graphical errors.

Screenshot of Wolfenstein 3D without any problems.

Success!

So is the problem down to the size of the long double? There are many different possible sizes, from 64-bit, 80-bit (MSVC <= 6 or GCC __float80), 96-bit (probably just 80-bit with padding), or 128-bit (GCC __float128). From my, er, uneducated trial and error modifications, I have decided that it is not likely to be the root cause. I don't know what is. Unfortunately, all three cores use at least a bit of x86 assembly, so __float128 doesn't work because it is a GCC specific extension.

Other FPU Cores

There are three implementations: an IEEE standard core (default), the one from WinUAE (the Amiga emulator), or one written in x86 assembly. The IEEE core seems to work with 64-bit, 96-bit or 128-bit long doubles. The WinUAE core only needs 64-bit doubles. The x86 core uses the native 80-bit registers for long doubles.

In order to use JIT recompiling to increase performance, you are supposed to use the IEEE core. Whilst fiddling around, I found that it was possible to use JIT on the CPU for any of the FPU cores, but you can only use JIT on the FPU with the IEEE core.

Conclusion

I have probably wasted a whole load of time following false leads, and have shown my stupidity several times, but at least the end sort of justifies the means. I personally have found that the x86 FPU core with CPU JIT to be the most stable on my PC, but YMMV. If you are compiling your own Basilisk II build and it is glitching the same way, try forcing 64-bit doubles in types.h, or maybe a different FPU core. Good luck!

Return to Blog Index