386 Xenix version 2.2.3 was finalized in late June 1988 (the newest files are timestamped June 27, 1988) and is thus one of the oldest surviving 386 PC operating systems.To put it in perspective, it was released at about the same time as IBM DOS 4.0 and several months before OS/2 1.1.
It is not the oldest since 386 Xenix 2.2.2 was released sometime in late 1987 (more or less coinciding with the release of 16-bit OS/2 1.0!), and there were possibly 386 Unixes from Microport and/or ISC available in mid to late 1987, though evidence is lacking.
At any rate, the search is for the oldest 386 PC OS, that is an operating system which runs on 386 PC compatibles, has at least substantial amount of 32-bit code in its core, is a self-contained system (i.e. no DOS extenders), can run 32-bit applications, and ideally uses paging. 386 Xenix checks all those boxes.
As described four years ago, a 386 Xenix 2.2.3 disk set exists (3½” 720K media) but is damaged. There are two variants, one containing raw images and one with TeleDisk images. The raw images rather obviously contain “freshly formatted” sectors (full of F6h bytes) in several places where there should be data. One of the more problematic victims of the corruption is the
/etc/init binary without which the system goes into a crash loop rather early in the second stage of the install (when booting from hard disk for the first time).
As luck would have it, there is
/etc/init8 on one of the International Supplement disks and it just so happens to be the same as the damaged
/etc/init (actually only 99.99% the same, but the only difference is a version string). With a non-crashing init, it’s possible to get a rudimentary booting system.
But wait, what about those TeleDisk images? Yes, they are weird. They have sectors missing, but they also have duplicate sectors, as many as five or six on certain tracks. The damage is not extensive, at most 1-2 missing sectors per disk, but on a 17-disk set that adds up to a lot of trouble.
To be clear, the TeleDisk images are not corrupted at all. The structure of the TeleDisk images shows no signs of corruption. In fact the data is protected with CRCs, and those all check out. There is also not a slightest hint of any of the sectors present in the images being corrupted.
It is apparent that the duplicate sectors must be a bug in some version of TeleDisk. It is possible to have multiple sectors with the same ID on a track, and it is something that TeleDisk can handle. But Xenix wouldn’t know what to do with that, and more importantly, it is not possible to have that many sectors on a track. A 720K disk simply cannot have 13 or more 512-byte sectors on a track, there is just no room for that. (This also excludes the possibility that TeleDisk just faithfully represented corrupted disks.)
It is hard to tell if the problem was with the disks, the drive, or the TeleDisk software alone. Most likely it was a combination of TeleDisk being confused by flaky drive and/or floppies.
Anyway, it turns out that the TeleDisk images contain a few sectors that were missing from the raw images, so yay for that; 3 or 4 sectors were recovered that way. But several sectors are truly missing, possibly gone forever.
The restoration process will be described in a follow-up post, but comparing the disk contents with other Xenix 2.2.x releases brought back several more sectors from files that were unchanged in the other versions. The real challenge was reconstructing the sectors which were just missing entirely.
The Xenix 386 2.2.3 kernel is a bit old, so of course it has trouble here and there. Like most other old systems, it is entirely unable to deal with any kind of IDE geometry translation (and it only comes with a wd1010 aka AT disk driver). That limits the size of IDE disks to about 500 MB (and there is only IDE support in this Xenix version).
The next hurdle is the wd1010 (functionally synonymous to IDE/ATA) driver which has trouble on virtualized systems and possibly fast IDE systems. It contains the following odd code for submitting the INITIALIZE DEVICE PARAMETERS command:
L1: push dword [11c4ch] ; command push 1f7h ; IDE command register call _iooutb add esp, 8 push 1f7h ; IDE status register call _inb test al, 1 ; is error bit set? jne L1: ; retry if so
The reason why that doesn’t work is that the status register which is read immediately after submitting the command will clear the interrupt, and that will stop the disk driver state machine (because the interrupt handler needs to be executed to advance it).
The writer of that code clearly assumed that after writing the command register, it will take some non-negligible amount of time before the command is completed. The question is then what the author expected to see in the status register if the command potentially hasn’t even started executing! The code seems to be a workaround for some specific problem. It is unnecessary, and logically incorrect, even if it caused no apparent harm on at least some systems (interestingly, Xenix 2.3.1, only a few months younger, has no such problem). One possible solution is patching the Xenix kernel to read the alternate status register (3F6h) instead of the status register (1F7h), the difference being that reading the alternate status does not clear interrupts.
Another troublemaker is the serial port driver. It likes to print the following error message:
garbage or loose cable on serial dev 1, port shut down
The problem is that the emulated serial port is “too fast” and outgoing data may move over the virtual wire as fast as it is written. The Xenix driver does not expect that and if it successfully writes 10 characters in a row from its interrupt handler, or more accurately if there are still pending interrupts after ten loops through the interrupt handler, it throws up its hand and goes off to sulk in the corner.
Fortunately it’s easy to patch out the 10-loop limit and get functioning serial terminals.
After finding a few missing sectors elsewhere and manually reconstructing some, the result is a fully functioning Xenix 2.2.3 with no known problems. It’s a System V derivative, but with a clear Microsoft influence—for example the compiler emits OMF object files and executables are in Microsoft’s X.out format (essentially a segmented a.out variant).
It’s a classic Unix OS, adapted for PCs. It handles at least up to 16 MB RAM, supports virtual consoles, it uses paging, it is fully compatible with existing 16-bit Xenix binaries, but also supports 32-bit applications. It’s a real 32-bit PC operating system.
So what other 386 PC OS has survived to this day? Anything from 1987?