Modifying Microsoft Flight Simulator 4 to run on three immersive monitors How I modified DOSBox and the original Microsoft Flight Simulator 4 from 1989 to run on my immersive multi-display flight simulator set up. Update: June 2019 I now have a project page describing the many plugins and apps I have written for X-Plane and home cockpits. FS4 from 1989 Back in 1989, I was introduced to Microsoft Flight Simulator version 4. I thought it was incredible that I could fly around in a 3D environment, and this was something that got me interested in 3D graphics. However, I always had trouble flying the plane, because I only had a 14 inch CRT monitor, and you had a small field of view out the front of the plane. Since you couldn't see left or right, you were missing important flying cues, and it made flying much more difficult than in a real plane. I've seen some people try to use joystick hats or head trackers, but those always require you to look forwards even if you are virtually looking another direction - it is not intuitive for your brain to understand. So at the time I realized this was a problem, and I'd seen commercial immersive flight simulators in books, but they were too expensive, so I had to wait a few decades for things to become a bit more affordable! However, recently in 2017 I built my own immersive indoor flight simulator, using three large 42" televisions and X-Plane 11. This provides a much more realistic experience, and it is finally the flight simulator I was always looking for.

Microsoft Flight Simulator 4 from 1989, running at 640x350 DOSBox emulation in 2017 I'm a bit of a fan of emulators, so I used DOSBox on Linux to start up Microsoft Flight Simulator version 4 on a big monitor. It is amazing how low-res 640x350 is when displayed on a large 1920x1080 display, although you didn't notice it as much on a 14 inch CRT, which is smaller than many laptops these days. DOSBox supports emulating old analog PC joysticks, so I was able to configure FS4 to use my yoke and throttle. I always wanted to have one of those as a kid as well, and it worked great. While there wasn't much for graphics, it worked with a nice smooth frame rate, and it was pretty cool. But then I started to think about how even better it would be to run FS4 on all three monitors, just like I'm doing with X-Plane now. In 1989, computers only supported a single small display, and definitely not three of them! The concept of using a cluster of machines was done in expensive commercial simulators even back then, but FS4 didn't support anything like that. FS4 supports the ability to do multiplayer over an RS-232 cable. However, there is no way to set the camera viewpoint to the remote aircraft, so that was not going to be a help in building a cluster. There was nothing else built into FS4 that could support what I wanted, and DOS machines back in 1989 didn't support networking or anything else either. All seemed hopeless with this ancient software. DOSBox memory dumps Since I'm running the whole thing within a DOSBox emulator, I now have access to networking and modern tools to try and achieve what I want however. DOS games were very simple in that they didn't use much of an operating system, and wrote everything directly to unprotected memory. I figured that the developer would have written the X Y Z and Heading Pitch Roll values to some kind of struct in a consistent location in memory. If I could work out the memory location of where the aircraft state is stored, I could intercept this with DOSBox, send UDP packets over the network to other DOSBox instances, and write to those same memory locations. When you dig around in the DOSBox source code, there is a simple 16mb array of 8-bit chars named MemBase in memory.cpp. That is the entire memory space of the DOS machine, and only the first megabyte is actually used. So I started up FS4, paused the game, and attached gdb to the live process and saved out a copy of the MemBase memory array. Then I entered slew mode, which allows you to reposition the aircraft at whatever coordinates and orientation that you want. Then you use gdb to again save the MemBase memory array. I noticed that FS4's coordinate system seemed to have a maximum of 65535 so it was probably using 16-bit integers, and it didn't have support for floating point arithmetic since that was not common in 1989. So I changed the coordinates to values like 0, 1, and 65535, in the hope they would be more obvious in the memory dump as 0x00, 0x01, and 0xFF. I was also able to reload the dump file back into memory, and I could successfully move the viewpoint back to where it was. So in theory, this could work. I used the Linux cmp command to compare the binary dump files. You would be surprised how little actually changes between the dump files, and so I went through the differences to see if I could spot anything. I didn't find any of the 0x00, 0x01, or 0xFF changes that I was expecting, which seemed strange. After studying lots of diffs, I realized that FS4 must be using a different encoding scheme than just raw integers. I needed a new strategy. I decided to try just fuzzing over the memory locations that did change to see which one would have an effect. I modified DOSBox to add the ability to receive commands from standard input, and wrote 0x00 over those locations one-by-one with a delay between them. FS4 would crash and go crazy for some values, so I would restart it and keep looking for the desired effect. Eventually I found that the viewpoint would change drastically when I wrote to 0x28D0. Ahah! I had found a memory location that changed the viewpoint, but the encoding was super weird and didn't work the way I expected. Writing zeros moved the aircraft to +16384 instead of +0 or +32767. North and East encoding After trying a whole bunch of different values, I worked out how North and East coordinates are represented. The first two bytes are the fractional component, and the second two bytes are the integer component. Intel x86 is little endian, so the least significant byte goes first. Here are some examples of raw memory locations and how they map to coordinates:

0xFFFF = +16383d

0x0000 = +16384d

0x0101 = +16641d

0x1010 = +20496d

0x2020 = +24608d

0x0020 = +24576d

0x2000 = +16416d



0x0030 = +28672d

0x0040 = +32768d

0x0050 = +36864d

0x0060 = +40960d

0x0070 = +45056d

0x0080 = +49152d

0x0090 = +53248d

0x00A0 = +57344d

0x00B0 = +61440d

Here you can see that 0x00C0 is where the origin of the coordinate system is!

0x00C0 = +00000d

0x00D0 = +04096d

0x00E0 = +08192d

0x00F0 = +12288d

0x00FF = +16128d



The fractional part made a bit more sense, you can tell it is just the 16-bit value that is then later divided by 65536. Here are some examples:

0xFFFF = 0.9999

0x8080 = 0.5020

0x00FF = 0.9961

0xFF00 = 0.0039

Angle encoding Now that I understood what was going on, understanding the angular values was much easier. These are also using an integer representation, where the first byte is a fraction divided by 256 and added to the main angle. The second byte is the most significant part, and there are 256 units in a full circle. Here are some examples of the most significant value:

0x00 = 0 degrees

0xA0 = 180 degrees

0xFF = 358.60 degrees Memory layout It turns out that the North, East, Altitude, Heading, Pitch, Roll values are all stored together in a contiguous memory area from 0x28D0 to 0x28E1. Here is a breakdown of every byte and what it does:

Offset Axis Value Order 0x28D0 East Fraction LSB 0x28D1 East Fraction MSB 0x28D2 East Integer LSB 0x28D3 East Integer MSB 0x28D4 Altitude Integer LSB 1 0x28D5 Altitude Integer 2 0x28D6 Altitude Integer 3 0x28D7 Altitude Integer MSB 4 0x28D8 North Fraction LSB 0x28D9 North Fraction MSB 0x28DA North Integer LSB 0x28DB North Integer MSB 0x28DC Pitch Fraction LSB 0x28DD Pitch 256 units to a circle MSB 0x28DE Roll Fraction LSB 0x28DF Roll 256 units to a circle MSB 0x28E0 Heading Fraction LSB 0x28E1 Heading 256 units to a circle MSB Synchronizing over the network Given that there are exactly 18 bytes of memory from 0x28D0 that contain everything I need, it is pretty easy to send this to another machine. I created my own thread in DOSBox that uses memcpy() at a 10 msec interval to capture the state of this 18 bytes of memory. In theory, this is unsafe without synchronization against the main emulator thread, but it worked well enough for this experiment. I then take the 18 bytes of data, and transmit it as two UDP packets to the localhost network interface with different port numbers. I tried using broadcast packets, but it is not possible to have two processes on the same machine listening on the same port number. After implementing the receiver code in two separate DOSBox instances, I have now managed to do a one-way push of those 18 bytes from one process to two others. Now they are perfectly synchronized, and you see three FS4 instances all showing the same viewpoint! Configuring the viewpoint FS4 has keyboard shortcuts that allow you to take a viewpoint and make it look left or right. So you can manually set the left and right DOSBox instance running on each monitor to look in the correct direction. The remaining challenge is configuring the field of view, which can only be varied with keyboard presses, and not as a specific angle. It turns out that FS4 has the ability to do both large and fine changes to the field of view, but we need to work out what that is. The solution is to use slew mode and adjust the orientation of the aircraft so that Heading, Pitch, and Roll all have significant values - you can now see the horizon has large gaps in it across the monitor edges. But if you gradually apply the same zoom in/out controls to each monitor, one step at a time, you will eventually see the horizon become a straight line, and now you have the correct field of view programmed in. Automatic startup While it is possible to move the windows to the correct monitor and then use the keyboard to make all the changes to the viewpoint, this is very tedious and it is easy to make mistakes. I wrote a script using wmctrl and xdotool which allows me to automate the placement of the windows and then to send the necessary keystrokes. Now everything starts up as quickly and possible, and it is very reproducible every time I start things up. Video This video shows the synchronization taking place across the three monitors, and you can see that as the aircraft flies around, everything works just as well as it works on X-Plane 11 right now. This is the flight simulator I always wanted as a kid, although it is 28 years late! But it is so much fun to see software written 28 years ago that was never intended to be run this way, actually work on an immersive flight simulator like this!

Source code I made a number of changes to DOSBox to implement my own debugger so I could do the fuzzing more easily, and then to implement the UDP transmitter and receiver. I also added a new stretch mode that will take any DOSBox window and make it fit exactly the desired dimensions, even if it breaks the aspect ratio. While there are many forum postings claiming this should work, it never worked and the source code did not seem to not allow it either. So with my patches, now I can ensure there are no seams between the windows, and I can pick any resolution that I want. However, the default 640x350 used by FS4 most closely matches the aspect ratio of my 1920x1080 monitors, and the round instrument dials are still circular. You can use the 800x600 driver that came out in the early 1990s, but the aspect is not as nice. You can download my patched version of DOSBox here on github. There is a script apt-get-deps that will download all the necessary dependencies for an Ubuntu 16.04 machine. You will need to provide your own FS4 directory with binaries of the game. I still have my old disks from when I bought FS4 in 1989, but you can also find it on abandonware sites on the Internet as well. There is great web site PlanetMic where there are still fans playing the old FS4 on modern PCs, I hope they enjoy my project! Photos