Episode 2: Soft Considerations
As we've already dived into the hardware in the previous episode, it's time for some early considerations on the software.
The Grand Design
What we're attempting to do is a port of Maze War, generally considered to be the first multiplayer first person shooter. The game was first implemented on the Imlac PSD-1 as Maze by Steve Colley, Howard Palmer, and Greg Thompson in 1973 (ca.) and ported to the Xerox Alto by Jim Guyton, Bruce Malasky, et al. in 1974/75, where it became known as Maze War, a name that stuck to the various ports and implementations to follow.
Here's a video of Maze on the Imlac PDS-1:
And this is Maze War on the Xerox Alto (as of 1979):
It seems, the Alto-implementation has become the standard template of the game with its iconic eyeball representation of the respective other player and the always-on on-screen map of the maze.
Historical Note
Maze War may have been the first multiplayer game featuring a pseudo-3D first person perspective, but it's not the first attempt at a multiplayer shooter game drawing the scene from individual player's positions. This was already attempted a decade earlier on the DEC PDP-1 with Spacewar! 4.4 in May 1963 by D.D. "Monty" Preonas and Joe Morris (a mod of standard Spacewar! 4.2 by D.D. "Monty" Preonas).
Compare Spacewar! 4.4 in emulation (online, requires a keyboard, larger screens and/or full-screen mode recommended) and the discussion of the code and its implications.
Check List
Obviously, Maze War is a graphics game (using vector hardware like the PDS-1 or or bitmapped one like the Alto). The Kyocera Siblings feature a graphics mode of 240 × 64 pixels. This isn't exactly high resolution, but it should be sufficient for a stripped down version using a somewhat smaller maze. (Maybe, we'll have to limit the depth of field and/or the the longest passages in the maze to meet design specifications). — Check.
Importrantly, Maze War is a networked game (originally for two computers only, one performing as a serving host and the second one as its client). The Kyocera Siblings all feature serial connections via RS-232C (DB-25 connectors) with a rate of up to 19,200 baud. Microsoft BASIC 1.0 provides some nice commands to setup and control a connection. (While upstream is stable at the maximum rate, downloads use to choke at speeds above 1,200 baud. Usually, there isn't any problem at higher speeds with the first line of data, but as a line is complete, characters may be missed while the line is processed. We'll see, how this performs under program control. — I haven't tried yet. — However, 1,200 baud should be fast enough for exchanging player positions and a status.) — Check.
Maze War isn't only a FPS game as in First Person Shooter but also as in Frames Per Second, meaning, it should run reasonably fast. I want to do this in BASIC, just for the fun of it. Now, a 8085 at 2.4 MHz wasn't the fastest of processors, even in 1983, and BASIC isn't exactly known as the fastest of languages. But to our rescue, there are a few well-known guidelines to performant programming with Microsoft BASIC. Let's see, how far this will get us. — (Somewhat) Check.
The 10 Commandments for Performant Microsoft BASIC Programming
- Never, ever use numeric constant literals.
- Define often used variables early (MS BASIC searches from start to end).
- Have common GOTO/GOSUB targets at the start of the program (MS BASIC searches from start to end).
- Use integer variables where possible.
- Else, use single precision variables where possible.
- Do not have any spaces in your program.
- Do not have an identifier next to your NEXT.
- Use single-character shorthands where available (?,').
- Remove any comments.
- Pack statements where possible, e.g., DATA statements.
Regrettably, these rules also provide the perfect guide to Spaghetti Code.
To avoid Fear and Loathing at Casa (read: Macchina) Piccola I might go for some break in style by using modern aids, namely writing legible code (not least for the purpose of this blog) and a few RegExps later we'll have performant code on the target machine. We'll see …
Machine Specific Considerations
We've seen in the last episode how amazingly different the Kyocera Siblings are in their hardware layout, while generally providing the same core components at the same specs. All of the machines come with Microsoft BASIC 1.0 in ROM — and this isn't your spartanic Commodore version of Microsoft BASIC, it's a quite luxurious one featuring unheard-of keywords like ELSE and even some graphics commands. (If your last BASIC-machine was a Commodore, like the PET or the C64, you're up for some kind of cultural shock.) But the differences of the machines do not stop at the hardware-side, they also differ in some details of the BASIC implementation. Here, we show some exceptional luck in the choice of our project, since a number of these incompatibilities present a problem to our purposes. — So there will be some to explore.
Communications
The settings of the serial communications (RS-232C, in BASIC generally COM) may be set either in the "TELCOM" application or anywhere in BASIC, where the "COM:"-prefix applies. (Namely, any file commands and commands for opening a stream for input or output commands that take a file descriptor.) If not specified, the port defaults to the last setting used.
General Syntax
On the TRS-80 Model 100, the Kyotronic 85, and on the Olivetti M10, the syntax for the settings is a string of:
<b><w><p><s><x>
Where
<b> is the baud-rate,
<w> the word-length,
<p> the parity,
<s> the stop-bit, and
<x> the XON/XOFF line-status.
In Detail the settings are:
Baud Rate | 1 | 75 baud |
2 | 110 baud | |
3 | 300 baud | |
4 | 600 baud | |
5 | 1,200 baud | |
6 | 2,400 baud | |
7 | 4,800 baud | |
8 | 9,600 baud | |
9 | 19,200 baud |
Word Length (number of bits) | 6 | 6 bits |
7 | 7 bits | |
8 | 8 bits |
Parity | O | odd |
E | even | |
N | no parity | |
I | parity bit ignored |
Stop Bit(s) | 1 | 1 stop bit |
2 | 2 stop bits |
Line Status | E | enable (XON) |
D | disable (XOFF) |
E.g., 1200 baud, 8 bits, no parity, 1 stop-bit, XON is "58N1E".
NEC PC-8201A
The NEC PC-8201 is probably the most idolectic of the machines. Here we find the following syntax, taking 6 paramters (instead of the 5 of the other models):
<b><p><w><s><x><c>
Where
<b> is the baud-rate,
<p> the parity,
<w> the word-length,
<s> the stop-bit,
<x> the XON/XOFF line-status, and
<c> control according to shift in/out (CTRL + S / CTRL + Q).
In Detail the settings are:
Baud Rate | 1 | 75 baud |
2 | 110 baud | |
3 | 300 baud | |
4 | 600 baud | |
5 | 1,200 baud | |
6 | 2,400 baud | |
7 | 4,800 baud | |
8 | 9,600 baud | |
9 | 19,200 baud |
Parity | O | odd |
E | even | |
N | no parity | |
I | parity bit ignored |
Word Length (number of bits) | 6 | 6 bits |
7 | 7 bits | |
8 | 8 bits |
Stop Bit(s) | 1 | 1 stop bit |
2 | 2 stop bits |
Line Status | X | XON (enable) |
N | XOFF (disable) |
Control (shift in/out sequence) | S | affects control |
N | does not affect control |
E.g., 1200 baud, no parity, 8 bits, 1 stop-bit, XON, no shift in/out is "5N81XN".
*****
Just as a sidenote: And what about the additional SIO-ports, we've seen on the NEC machine in the previous episode?
From the post "SIO1 and SIO2 Information" by Michael (Hansen?), www.modd.net, we learn:
SIO1 and SIO2 are not really separate ports, just separate connectors that can be activated by means of a multiplex command to carry the COM: serial signal. The multiplex port is 144 decimal, 90 hex. You can enable SIO1 with the command
OUT 144,160
within a BASIC program. For SIO2, I am told that you would use
OUT 144,96
although I cannot personally vouch for that, having not tested that myself.
So, now we know this, too. (According to the same post, the multiplex settings are canceled whenever the TELCOM application is entered. Also, invoking TIME$ in BASIC will reset the multiplex settings. So we should check the status of the multiplex port using some like "X=INP(160):IF X=160 THEN REM OOPS, SIO1 NO LONGER AVAILABLE
".)
*****
—???—
So, since the syntax isn't only different in the order of the parameters but also partly in their value and the NEC even features an additional parameter (which may be ignored), how may we even attempt a cross-platform program for these machines?
A possible answer is in PEEKing address 1, which delivers a well known numerical value for each of the models. Based on resources found at the web and from own experiments, I may provide the following values for PEEK(1):
35 | ... | Olivetti M10 (Europe — is the US-version different?) |
51 | ... | TRS-80 Model 100 |
148 | ... | NEC PC-8201A |
Sorry, there is no known value for the Kyotronic 85.
Using this we may determine the model and setup the program to use the right parameters for each of the machines.
Et voilà, auto-configure.
Graphics
There are some nice graphic commands like, PSET and PRESET, and even an augmented LINE command providing also box-drawing capabilities, outlined or filled. Except that the NEC doesn't have a LINE command. On the other hand, the NEC features programmable characters and the others don't. What shall we do about this?
We may consider just using the PSET and PRESET commands for setting/clearing a single pixel. But these commands are terribly slow. A reason for this is that the processor has to identify and select the right one of 10 LCD driver chips, disable interrupts, read from and talk to the selected driver chip via a serial line, and finally re-enable interrupts.
So, what about the NEC and the LINE command? I understand that it's still there in ROM, it's just not present as a keyword. (A cause may be that NEC didn't like the confusion with the LINE INPUT command, or any other reason you may think of.) However, there is a machine language program to be found amongst ancient posts that re-implements the command, and we may use this or just use the appropriate ROM calls. But there is a draw-back to this approach, as we would have to clear the according portion of the screen first (technically writing zeros into each pixel location) and then draw the lines, we need. Effectively we would have to print the scene twice and we're not that optimistic about performance from the beginning.
Moreover, if we want to implement the iconic player-eyeball, we would have to fall back to PSET and PREST, which are — just terribly slow.
So, the NEC has programmable characters. If only the other machines would have them, too …
We could paint our scene at once (we're drawing only a fixed perspective using a limited set of lines and angles) at the best speed, there is …
Having a closer look at the workings of the LCD implementation, there may be a way to this: The processor communicates with the driver chip by reading and writing a bit-vector of 8 bits (one byte), representing a vertical column of 8 pixels, occupying any of the 240 horizontal positions of one of the 8 rows. There are ROM routines for selecting the appropriate driver chip and offset based on the current cursor position, and there's a ROM routine for reading/writing a full byte at once. This is also, according to the PC-8201A Technical Manual (NEC Corporation, 1984) how character representations are transferred onto the screen at all. There's even an old machine language program for the Model 100 to emulate a programmable character-set by these exact calls! Since we're going only for printing and not for typing, we may safe the overhead and go for the ROM calls immediately. We may even optimize the program for the mapping of the individual driver chips, thus probably being faster than using conventional PRINT commands. (This would render said approach even an interesting option for the NEC with its built-in facilities.)
As a bonus, we may consider to draw the iconic eyeball using character-based sprites (or bitmaps stored in the character-format). The character format is, BTW, 6 bytes of 8 bits, representing a character by a matrix of 8 rows of 6 pixels turned clockwise. Now we also know, why this would be.
So, we've found a viable approach, at least in the grand design. We'll try some character emulation and maybe have some special effects drawn ontop of this using the LINE command (or its equivalent in ROM calls). ROM addresses and any special syntax will be selected by identifying the model by PEEK(1).
But we haven't arrived at the end of incompatibilities, yet:
Cursor Positions
There is PRINT@ (on the German M10 PRINT§
– argh!) on most models of the line for locating the cursor at a given position of the current line, but there is LOCATE on the NEC to position the cursor anywhere on the screen (taking an X and Y parameter). And of course, no PRINT@ on the NEC — again. But there's also common ground, like ANSI escape sequences for cursor positions and well-known locations in the "house keeping" area of the RAM, working together with ROM calls.
ROM Calls
While we're talking of calling ROM addresses, even this isn't the same on all machines. While most use the CALL command (syntax: CALL address, A, HL — where A is an 8-bit value to be transferred to the accumulator and HL an optional 16-bit value to go to the HL-registers), the NEC uses the EXEC command (syntax: EXEC address — A, L and H are read from locations 63911–63913). Again, not exactly a joy-ride to program, but doable using our auto-configure trick.
Variable Defaults
Once again, the NEC is different, as its BASIC variables default to single precision, as opposed to the other models which default to double precision. (As a result of this, the NEC is considerably faster with simple programs, like, "FOR I=1 TO 100: PRINT I, I*I: NEXT
".) — Not much of a trouble for our project, as we will want to define our variables anyway (using DEFINT, etc), consistently with to the 10 Commandments for Performant Microsoft BASIC Programming (see above).
*****
Having to deal with this long list of incompatibilities, which are most, if not all, caused by the idolectics and idiosyncrasies of the PC-8201A, we may ask, why a honorable company like NEC Corporation (Nippon Denki Kabushiki Gaisha) would do this to us. I would suppose, to align the BASIC with the dialects found on other NEC-models (we may think here of the PC-8001/PC-8001A line introduced in 1979) and to integrate this model into a whole line of products. At least, I couldn't think of any other reasons. Apparently, cross-compatibility with the other Kyocera Siblings wasn't top priority. — Ayway, the PC-8201A is quite a clever little machine, just as it is.
*****
Now, that we have sorted this out, we're ready to go for some evaluative tests and some actual programming to sort out the details by drafting a mockup.
▶ Next: Episode 3: Drafting Time
◀ Previous: Episode 1: The Kyocera Siblings
▲ Back to the index.
2016-01-04, Vienna, Austria
www.masswerk.at – contact me.
— This series is part of Retrochallenge 2016/01. —