How the original Elite-A source was written, edited and compiled
There are four source discs for Angus Duggan's Elite-A, each of them packed with code and programs. In this deep dive, we're going to take a quick tour through the build process and file structure, and talk about how I converted them to build on modern computers, as seen in the accompanying repository.
IDE or word processor?
Angus created Elite-A back in the 1980s on contemporary hardware - specifically, on a BBC Micro with a 6502 Second Processor and disc drive. He also had Acornsoft's VIEW word processor fitted in ROM, and an assembler ROM that Angus wrote himself. I guess this setup is what you could call an Integrated Development Environment today... even though VIEW is a word processor, not a text editor.
Luckily, VIEW files are essentially ASCII text files. VIEW was written in the days before curly quotes, em-dashes, ligatures and all the other aspects of modern word processing that make them terrible choices for writing code, and although VIEW supports text effects such as bold, italic and so on, simply avoiding their use leaves you with a pretty functional text editor. The only oddity is the ruler, which VIEW inserts into the middle of the text to redefine the tab stops, so you might get the following appearing in your source code files:
or perhaps this:
The asterisks are tab stops, and apart from an ASCII 129 character at the start of the line (not shown here), the rulers are stored as normal characters in the body of the document. These examples are taken from the a.qcode source file, but there are lots of them dotted around throughout the source code.
Because Angus used his own assembler ROM to assemble the source code, he could easily add code to ignore lines like this. So although Acornsoft's flagship word processor might not seem like an obvious choice for coding from a modern perspective, it works pretty well, and for large sources it is certainly a lot more manageable than using the assembler built into BASIC, which the original authors of Elite used. The original version's source code is almost unreadably compact (see the About this project page for some particularly gnarly examples); Angus's source code, by comparison, is delightfully spacious, with just one instruction per line. What a luxury!
Source files and build process
The source files are split across four discs: there's one for the standard version, another for the 6502 Second Processor version, and two discs for the ship blueprints. Within these discs, most of the source code is split up into multiple source files, with the largest being a hefty 47K in size (when you consider that the unexpanded BBC Micro only has 32K in total, including the screen memory, this kind of file size is pretty luxurious by the standards of the day).
Each binary in the final game (e.g. 1.F, 1.E) has a corresponding main source file (e.g. a.dcode, a.icode). This main source file pulls in various other source files, using a GET directive to include those files into the assembly process. For example, the flight code binary in 1.F is generated by assembling the a.dcode file, which first includes a global variables file like this:
and after some initial code, it then contains these three lines:
GET "a.dcode_1" GET "a.dcode_2" GET "a.dcode_3"
which fetch the three named files and assemble them into the finished binary. The actual assembly is done via a star-command to Angus's assembler ROM:
*asm o "a.dcode" ":0.1.F"
which tells the assembler to load the source code file a.dcode and save the assembled output to the 1.F file on drive 0. There are similar steps for all the different source files, and once they are all done, the binaries are copied to the final game disc.
The standard game's source disc uses this kind of structure to build the final game binaries. There are four assembler source files (the main file and three includes) for each of the docked, flight and encyclopedia binaries, plus one more pre-assembled binary containing the ship blueprints for the ship hanger, which is confusingly called S.T, even though it's not the same as the S.T blueprints file on the final disc. There's also one more assembly file for the loader. Here's how the build process works:
- Docked code: a.tcode loads a.global, a.tcode_1, a.tcode_2 and a.tcode_3 to produce an interim binary called tcode, which is then concatenated with the S.T binary (which contains the ship blueprints for the ship hanger) to produce the final docked code in 1.D
- Flight code: a.dcode loads a.global, a.dcode_1, a.dcode_2 and a.dcode_3 to produce the flight code in 1.F
- Encyclopedia code: a.icode loads a.global, a.icode_1, a.icode_2 and a.icode_3 to produce the encyclopedia code in 1.E
- Loader: a.elite produces the loader code in ELITE
The source disc for the 6502 Second Processor version works along similar lines, though there are quite a few more source files:
- Parasite code: a.qcode loads a.qcode_1, a.qcode_2, a.qcode_3, a.qcode_4, a.qcode_5, a.qcode_6, a.qship_1 and a.qship_2 to produce the parasite code in 2.T
- I/O processor code: a.qelite produces the I/O processor code in 2.H
The a.qship_1 and a.qship_2 files contain the ship blueprints for all the ships in Elite-A, including Angus's new designs, as they are all loaded into memory in one go with the 6502 Second Processor version. For the standard version, however, the 23 ship blueprint files, S.A to S.W, are produced by programs and data files spread across two further discs.
Each ship blueprint is contained in a text file that lists the vertices, edges, faces and flight characteristics in a CSV format, with one data item per line. There are also two assembly source files, a.old and a.new, which contain this data in EQUB format (a.old contains the ships from the original Elite, while a.new contains Angus's new designs). There's also an assembly source called a.ships that imports both of these files and assembles them to a binary file called ships, and finally, this ships file is read by two BASIC programs called B.FILES and B.SHIPS, which produce the final ship blueprint files for the game disc. These files implement a very similar algorithm to the 6502 Second Processor version when it decides which ships to choose when arriving in a new system - see the deep dive on ship blueprints in Elite-A for more information.
It isn't clear on first glance how the CSV data is converted into the a.old and a.new files (assuming there is an automated process), and the way that B.FILES and B.SHIPS work together is not entirely obvious. Also, the S.T file that the docked code appends for the ship hanger is not the same as the S.T file produced by the ship blueprint source discs, so it isn't clear where this came from (though it could have been extracted from the original version and used as-is). A bit more analysis is needed here...
Converting the sources for modern computers
The format for Angus's assembly ROM is pretty close to that of a modern assembler like BeebAsm, so it isn't that difficult to convert the original sources to work on modern hardware. The biggest difference in terms of conversion is Angus's unique EQUA directive, which includes strings in the assembled code just as EQUS does, but it supports the vertical bar syntax from the BBC Micro's *KEY command as a way of entering control codes into strings. So, for example, |M is CTRL-M, or ASCII 13, while |! adds 128 to whatever follows it, so |!|M would insert ASCII 13 + 128, or ASCII 141. The text token tables are all encoded in this way in the Elite-A source, but BeebAsm doesn't support this format, so we need to convert EQUA to EQUB throughout.
The results can be seen in the accompanying repository, though the sources there have been annotated to within an inch of their life, and now bear little relationship to the original files (though they do produce the same output). For a start, I have changed the labels in the annotated code to bring them in line with those in the original Elite source code, only retaining Angus's labels for his new code. This makes it easier to see what Angus did to modify Elite into Elite-A, but it does mean the annotated code and the original sources feel like pretty distant cousins.
If you are interested in how I converted the original sources into BeebAsm-compatible code, there's a breakdown in the repository. There you can find the Python program that takes the original VIEW files and converts them into BeebAsm format, stripping things like VIEW rulers from the source, and converting Angus's EQUA operative into EQUB-compatible strings and bytes. The result is a BeebAsm version of the original source code, which can be assembled in exactly the same way as the original discs, but using a beebasm command on Windows, Mac or Linux, instead of a *asm command on a BBC Micro.
There are some other subtle points to note in the conversion, which you can read about in the repository, where you will also find "before" and "after" files.