Download FractInt for Windows Beta 5
The legacy code I’ve been working on lately is FractInt. FractInt’s most recent release is a source base that compiles in three ways: DOS (FRACTINT), Win16 (WinFract) and lunix/X11 (xfractint). The DOS code has gone about as far as any DOS program should go, and then some. The Win16 and X11 code has lagged behind the DOS code in some areas, mostly because people didn’t want to translate the 16-bit x86 assembly code for other environments.
In 1999, I studied the FractInt source base and suggested a set of changes that would move the DOS code into a Win32 environment more completely than the Win16 version had done. The Win16 version had made native Windows dialogs for all the UI and while this was nice, because the DOS version was still alive the changes kept getting made there and they weren’t back-filled into the Win16 version. The X11 version was a hybrid X11/curses program, which definately looked odd when you ran it. So there were divergent UI methods, with some of the UI code falling behind the DOS code which always got all the changes. In addition the X11 code also lagged behind the DOS code in functionality, but in ways that were different from the Win16 code.
FractInt is a large C program. Not C++, but C. It has a large quantity of global data and the code tends to be organized in weird ways in order to accomodate the 640K limit on an x86 segment and it uses an overlay approach to memory management. The overlay model created incentives for weird aliasing of data and weird placement of functions and global data. So yeah, its a big piece of legacy code that has its fair share of warts and tumors that it has grown over the years. (FractInt’s first release was in 1988, I believe.)
However, FractInt also has a lot of code that’s worth saving. It computes over 150 fractal types, has a variety of image decomposition methods that can be applied to most fractal types, has a means of computing images on arbitrary formulas with its own builtin formula parser, supports arbitrary precision arithmetic on some fractal types, supports resuming of partially completed fractals, has lots of coloring options for inside and outside regions of fractal sets and many other useful features.
Since noone had acted on my partial implementation from 1999 to move FractInt forward and I recently completed a big multi-year personal project, I took on the task of finishing my partial implementation into a complete implementation for Win32. (An updated xfractint release will come later.) This is an implementation for Win32 that steals a trick that xfractint used to sneak a message loop into the polling I/O model currently in FRACTINT. The code polls the keyboard often enough that sneaking the message loop into the keyboard processing works somewhat decently. Along the way, a major restructuring of the entire code base has been done to remove all the DOS-isms: segments, near pointers, far pointers, interrupt calls to the BIOS and the 16-bit assembly language. Furthermore, restructuring passes have been made on the source code to replace ‘magic numbers’ with symbolic names, naming global data and file scope data with prefixes to indicate their storage class, eliminating fallback code for systems with no FPU accelerator (386 and earlier) or even just 80287 FPU specific code, and sanitizing function names wherever possible. There probably isn’t a single line of code in the entire source base that I haven’t touched in one way or another. In ‘legacy code’ terms I did a hell of a lot of “Rename Method”, ”Rename Variable” and “Rename Parameter” refactorings along with a few “Extract Method” refactorings along the way.
Most of these changes have been refactorings because they are not intended to change the behavior of FractInt. If they don’t make any difference to the end user, you might ask what good are they? Well, as I read through the source code in an attempt to assimilate enough of it to introduce a driver abstraction, there were a number of barriers to understanding that were just an artifact resulting from the organic growth of fractint. There’s too many globals to keep in your head. When you’re looking at a piece of code in isolation, and it uses variables not defined in the local scope, you can’t easily tell if its accessing global data or file scope data. Furthermore, knowing what that global data was for or what values were valid was often a question that could only be answered by searching the entire code base for where its used and reverse engineering back out the semantics of the variable in question. So marking all global and file-scope data with a special prefix (g_ and s_, respectively) at least helps you know at a glance what kind of data any particular statement is manipulating. Giving things clearer names helps other people to understand what the data is about. Defining symbolic names for the valid values helps you see that this piece of data is treated like an enum with a set of specific values, that piece of data is treated as a set of bitflags with that set of flags, and this other piece of data is treated as an integer value.
The variable names, the style of programming that communicates through side-effects on globals and mostly the antiquated programming environment and the large source base all combined to create a giant set of barriers that intimidated most programmers from contributing to FractInt. It was large. It was hard to understand and hard to debug. Reading the source code was sometimes just painful. Monster functions that go on and on and on in a giant nested thicket of switch, if, and while statements. Who wouldn’t be scared of this code? Even if they weren’t scared of it, would they want to add code into a source base that was so strangled by 16-bit DOS memory constraints that by simply adding a function to a file they might break something?
So this release that’s currently at beta 5 will serve two purposes. First, will be the port to Win32 bringing along as many of the DOS features as is possible. The main things that will be missing compared to the DOS version are the integer fractal types written in assembly language. Eventually we may get those back — there are a lot of RISC architectures out there that have screaming integer performance but not so good floating-point. So integer math is still an important speedup technique on some architectures. We just need the integer code converted from x86 assembly into C code so that it can be compiled for those architectures.
Second, the source code itself will be a little more presentable. It still communicates through global variables, it still has a polling I/O model and there’s still the occasional Monster Function that goes on for too long, but its better now than it was before. There’s too much work to “fix” everything at once, but I can give you an idea of where FractInt is headed:
- Transition to C++ from C so that object-oriented methods can be used
- Separation of the compute engine from the UI
- Change to an event-driven I/O model from a polling I/O model
- Native UI for Win32 and X11 platforms instead of emulating DOS text screens
You’ll notice that except for that last one, these sweeping changes to the code base aren’t even visible to the user. Well, I might extend an existing fractal type or add a fractal type or two along the way, but most of these changes are needed to re-invigorate the FractInt infrastructure. It needs the kind of organization that lends itself to more contributions from others in the Stone Soup Group tradition.