The other day I started wondering why certain old versions of 286 and 386 XENIX look a bit weird in emulation:
The characters are cut off, because XENIX sets up an EGA text mode with 8×14 character matrix but uses 8×16 VGA fonts.
Investigation showed that the XENIX console driver rather closely follows the inner workings of the EGA BIOS, no doubt because finding out exactly what IBM’s EGA BIOS did was not difficult.
Specifically, text modes normally access memory in EGA/VGA plane 0 where character codes and attributes are stored. Fonts are stored in plane 2 and the video hardware needs to be reprogrammed to let the host CPU access that plane. For whatever reason, the EGA BIOS uses a rather heavy-handed but very straightforward method: It defines two “fake” modes in its internal mode table, namely mode entry 0Bh for color and 0Ch for mono modes. The EGA BIOS simply internally sets mode 0Bh or 0Ch, uploads the font bitmaps, and then sets the real desired mode.
XENIX 2.2 uses the exact same method. If the BIOS does not fill out mode table entries 0Bh/0Ch, XENIX will be unable to program the fonts and the original VGA 8×16 font will remain in place, but there won’t be other ill effects.
So obviously the BIOS could just supply the EGA compatible mode table entries 0Bh/0Ch. And indeed that helps with some XENIX versions, such as 2.2.3c, which now gets the right 8×14 font:
But oops! With other XENIX versions, such as 2.2.3b, the result is much, much worse:
Why would that be? Finding out requires a little more digging…
First off, the problem was documented by SCO. Specifically XENIX 2.2.3 was documented to work with Compaq’s VGA, but not with IBM (ISA) or Olivetti VGAs. What was so special about Compaq VGA cards? As it turns out, nothing special per se, but it had to do with exactly how XENIX programmed text mode fonts. The logic is is not particularly difficult to analyze because the console driver is supplied in object form as
cnscreen.o, included in
The BIOS mode tables mentioned above are easy for XENIX to find. All it needs is the pointer at 40:A8 in the BIOS data area which addresses the video parameter/save pointer table on EGA and VGA cards. The first entry in the pointer table points right at the mode parameter table, and the format of that table remained consistent across EGA and VGA BIOSes. Reading the table can be easily done from protected mode because real-mode far pointers are trivial to translate to physical addresses.
A much bigger problem XENIX had was finding the font bitmaps supplied by the BIOS. There is no convenient pointer that XENIX could read once it’s in protected mode. In retrospect, XENIX should have either called INT 10h/11h, AL=30h to figure out the font pointers while it was still in real mode, or it should have supplied its own fonts.
Instead, when XENIX was extended with EGA support, it was written to assume IBM EGA BIOS behavior: When a text mode is set, interrupt vector 43h points to the 8×8 font. Additionally, the fonts in the EGA BIOS are laid out such that the 8×14 font is first, immediately followed by the 9×14 extension, and the 8×8 font is last. When the XENIX console driver is initialized, it looks at the INT 43h vector, subtracts 0F30h bytes (the size of 8×14 font plus 9×14 extension), and assumes that that’s the 8×14 font.
On more or less any VGA, this logic breaks down, for two reasons: The INT 43h vector may not point to the 8×8 font, and the fonts are rarely laid out the same way as on an EGA.
There are some versions of XENIX (this is known to include 2.2.3b) which have added support for those above mentioned Compaq VGAs. The code looks strange at first glance but fortunately I’ve been able to cross reference it with a dump of the old Compaq VGA BIOS available on bitsavers.
The fonts in the Compaq VGA BIOS are laid out like this (the relative offsets are what XENIX cares about):
|Font||Rel offset (hex)||Abs offset (hex)|
That is exactly what XENIX 2.2.3 expects. If the system has a VGA, INT 43h will (XENIX hopes) point at the 8×16 font, the 8×14 font is at relative offset 1144h, and 8×8 font at relative offset 2072.
Sadly, that’s not the case on, for example, IBM’s ISA VGA (aka PS/2 Display Adapter). In IBM’s VGA BIOS, the fonts are laid out as follows:
|Font||Rel offset (hex)||Abs offset (hex)|
The fonts are ordered completely differently, and the assumptions XENIX makes about the Compaq VGA BIOS spectacularly break down.
In XENIX 2.2.3c (and presumably also in supplements xnx084/xnx086), SCO implemented a rather interesting solution which turned out to work reasonably well. After surveying the VGA landscape, SCO presumably concluded that their then-current approach of assuming certain layout of the font tables in VGA BIOSes was a lost cause. The fonts can be pretty much anywhere in a VGA BIOS, in any order, and don’t necessarily even have to be grouped together.
Someone at SCO then said down and thought, OK, we have no chance of guessing where the fonts are. But we can find them. Even though the character glyphs don’t have to (and in practice, don’t) all look the same across BIOS implementations, there are a couple of graphical characters that do, or at least really should, look the same.
XENIX 2.2.3c (namely function
cnfindfont) expects a BIOS to provide fonts conforming to IBM PC code page 437, and looks for the “full block” character at code point DBh. This character should be represented as a sequence of FFh bytes. The length of the sequence in bytes equals the height of the font. The code looking for fonts does a few more “spot checks” for specific bytes in a couple of nearby locations. If the heuristic is satisfied,
cnfindfont calculates the starting address of the font and returns that to the caller.
The heuristic would be easy to defeat, and perhaps was defeated by BIOSes with fonts not using code page 437. In practice the font search worked quite well on most systems.
It’s worth mentioning that if
cnfindfont is unsuccessful, it returns a NULL pointer. There is no attempt at error checking, and in such case, the kernel will probably crash during initialization, quite possibly with a blank screen. That was, again, documented by SCO.
With XENIX 2.3 (tested with XENIX 386 2.3.1a), SCO reworked the console driver to add full VGA support and did what they should have arguably done from the beginning—fonts were now included. I believe XENIX 2.3 still tries to find BIOS fonts, but has a fallback in case BIOS fonts can’t be found for whatever reason.
XENIX 2.3 also introduced the
vidi command which allows the user to change the console mode, including CGA/EGA/VGA text modes. Support for dual color/mono adapters was also reworked relative to XENIX 2.2.
All in all, the XENIX 2.2 approach shows the limits of compatibility. The logic used for EGA worked, but relied on a high degree of IBM compatibility. The Compaq VGA was not compatible enough and XENIX broke down. SCO then added support for the Compaq VGA, but without validating that the system does in fact have a VGA card made by Compaq. SCO relied on internal implementation details (exact layout of fonts and INT 43h contents on entry) that were different on other VGA systems. SCO then added a heuristic to find fonts in a video BIOS, but even that probably wasn’t 100% reliable. It was clearly a learning process.
Addendum: XENIX 2.1
XENIX 2.1 may end up looking exactly the same as XENIX 2.2, but fonts won’t get corrupted. XENIX 2.1 has somewhat limited EGA support, in that it can deal with all the different EGA configurations (CGA monitor, MDA monitor, or hi-res EGA monitor) but does not even attempt to reprogram fonts.
The assumption with XENIX 2.1 seems to be that XENIX will set the same text mode the BIOS did, and therefore the BIOS will have uploaded the right font into video memory.
I believe that logic fell apart when SCO started adding support for graphics on XENIX (CGI libraries and more); the uploaded fonts were likely to be overwritten in graphics modes, and XENIX needed some way to correctly program them again. It’s just that the implementation in XENIX 2.2 was a bit too optimistic.