Return to Suhalia Noctis

Beginning the archaeology

Early forays into the Noctis codebase were rarely straightforward. Shorter sessions were undertaken on my phone on the perennially tardy beloved N Muni line on the way to the office. Longer ones often featured a beverage at a back table at Vivarium late at night after work. My main goal in the early days was to see if I stood a chance at deciphering the original program. So without a better plan to get my bearings than just diving in, I downloaded the source and started reading.

The repository is about 300 files in a flat directory. You can browse it for yourself in its entirety here:

open the complete source browser ↗

If you go spelunking, you can find goodies in there ranging from build artifacts to audio experiments to miscellaneous IDE configs. On my first excursion, I skimmed for config files, which seemed like a reasonable place to start.

Two things greeted me in NOCTIS.MAK:

CC = bcc +NOCTIS.CFG

LIBPATH = C:\BC.31\LIB
INCLUDEPATH = C:\BC.31\INCLUDE

.cpp.obj:
  $(CC) -c {$< }

First, the game is written in C++. Second, that BC means it expects a so-called “Borland compiler”. Now it had been a few years since I had written C++, and I was not in the least familiar with a pre-standardization Borland 3.1 dialect of it, so I made a hasty retreat to the reference materials for some brushing up. A few notes right at the top:

Today it is easy to imagine C++ as a single, settled thing. In 1992 it was not. The first international C++ standard was still six years away, and Borland was one of several companies (alongside fellow titans of the era Watcom, Zortech, and IBM) maintaining its own compiler, libraries, and extensions. These implementations were not always identical. A program written for Borland could therefore contain perfectly reasonable C or C++ alongside features that another compiler would not understand.

It had been a long time since I had contemplated a divergent compiler. For the non-technical reader, the compiler is the program that reads source code and turns it into machine code. You generally like to imagine that this task is performed reliably and mechanically, and that you will not have to think about the many decisions it is making under the hood, like how its types occupy memory, how functions call one another, and which instructions the target computer will execute. Having disagreeing compilers is kind of like if saying “good day” in Vermont meant “hello” and in Minnesota it meant “I have a knife!”

But there was another complication, too. Some of the strange-looking code did not originate with Borland at all, but with the machine Borland was being asked to target. Noctis was built for DOS-era x86 computers, whose segmented memory gave programmers all manner of difficult puzzles to deal with.

While I nursed another cola pinched from the Vivarium refrigerator, it became clear that the quirks were hitting us from three directions: a pre-standardization programming language, an extinct compiler, and a very old school target surface. This was the least auspicious start imaginable.