The race for PC-compatibility
Since the mid-80s, there have been many clones of the original IBM PC. IBM themselves also made new-and-improved versions of the PC, aiming for backward-compatibility. DOS itself was more or less a clone of CP/M, and in the CP/M-world, the main common feature between machines was the use of a Z80 CPU. Other hardware could be proprietary, and each machine would run a special OEM version of CP/M, which contained support for their specific hardware (mainly text display, keyboard and drives), and abstracted the differences behind an API. As long as CP/M programs would stick to using Z80 code and using the API rather than accessing hardware directly, these programs would be interchangeable between the different machines. The lowest level of this API was the Basic Input/Output System, or BIOS (note that in CP/M, this was not stored in a ROM, but was part of the OS itself).
Since DOS was based on CP/M, initially it was meant to work in the same way: a DOS machine would have an Intel 8086 or compatible CPU, and the BIOS and DOS APIs would abstract away any hardware differences between the machines. Each machine would have their own specific OEM release of DOS. As people quickly found out though, just being able to run DOS would be no guarantee that all software written for the IBM PC would work. In order to get the most out of the hardware, a lot of PC software would access the BIOS or even the hardware directly.
So clone-builders started to clone the IBM PC BIOS, in order to improve compatibility. The race for 100% PC compatibility had begun. Some of the most troublesome applications of the day included Microsoft Flight Simulator and Lotus 1-2-3. These applications would become a standard test for PC compatibility.
Did they succeed?
By the late 80s, clones had reached the maturity that they would generally run anything that you could throw at them. The OEM versions of MS-DOS would also disappear, as a single version of MS-DOS could run on all PC-compatibles.
But how compatible were all these PCs really? Were they functionally identical? Well no. But this was a given in the world of PCs and DOS. The different IBM machines and PC-clones were ‘close enough’, and software was written in a way that 100% hardware equivalence was not required. It was a given that there were different types of CPUs, different speeds, different chipsets and different video adapters. So software would settle on a certain ‘lowest common denominator’ of compatibility.
But it is even worse than you might think at first. With our demo 8088 MPH, we have already seen that even clones that use an 8088 CPU at 4.77 MHz and a CGA-compatible video adapter aren’t compatible enough to run the entire demo. But beyond that, even IBM’s own hardware isn’t entirely consistent. There are two different types of CGA, the ‘old style’ and ‘new style’, which have differences in the colour output.
Beyond that, IBM did not always use the same 6845 chips. Some IBM CGA cards use a Motorola chip, others may use 6845s from other sources, such as Hitachi or UMC. Beyond that, there are different revisions of the 6845 chip from Motorola as well. Which would not be that bad, if it wasn’t for the fact that they may have slightly different behaviour. In the case of 8088 MPH, apparently all our IBM CGA cards used a Motorola 6845 chip, which supported a hsync width of ‘0’, which it translated to 16 internally. Other 6845s would not have this behaviour, and as a result, the hsync width actually was 0, which meant that there effectively was no hsync, and the monitor could not synchronize to the signal.
Another thing I have already mentioned before, is the 8259A Programmable Interrupt Controller. There were two main problems there:
- There are two revisions of the 8259A chip, models before and after 1985. Auto-EOI mode does not work on the earlier chips when in slave mode.
- A PC-compatible machine can have either one or two 8259A chips. The AT introduced a second 8259A to support more interrupt levels. As a result, the first 8259A had to be set to ‘cascaded’ mode. Also, in early PCs, the 8259A was used in ‘buffered’ mode, but in the ATs it was not.
And I also briefly mentioned that there is a similar issue with the 8257 DMA controller: early PCs had only one 8257, the AT introduced a second DMA controller, for 16-bit transfers.
I also gave the IBM PCjr (codename: peanut) a honourable mention. Like early PC-clones, its hardware is very similar to that of the PC (8088 CPU, 8253 PIT, 8259A PIC, CGA-compatible graphics), and it runs PC DOS, but it is not fully compatible.
I have recently obtained a PCjr myself, and I have been playing around with it a bit. What I found is that IBM made an even bigger mess of things than I thought.
As you might know, the IBM PCjr has advanced audio capabilities. It uses a Texas Instruments SN76496 sound chip (also used by Tandy 1000 machines). There has been an attempt by James Pearce of lo-tech to create an ISA card to add this functionality to any PC. I have built this card, and developed some software for it, and it was reasonably successful.
One thing we ran into, however, is that IBM chose port C0h for the SN76496. But for the AT, they chose the same port C0h for the second DMA controller. This caused us some headaches, since the card would never be able to work at port C0h on any AT-compatible system. So, we have added a jumper to select some additional base addresses. Tandy had also run into this same issue, when they wanted to extend their 1000-range of PC-compatibles to AT-compatibility. Their choice was to move the sound chip from C0h to 1E0h, out of the way of the second DMA controller.
This wasn’t a very successful move however: games written for the PCjr or early Tandy 1000 were not aware of the fact that the SN76496 could be anywhere other than at port C0h, so it was just hardcoded, and would not work on the new Tandy. So we had to patch games to make them work with other addresses.
But as I experimented a bit with the real PCjr, I also ran into another issue: the keyboard. The PCjr uses a wireless keyboard, so it has an entirely different keyboard controller than a regular PC. In order to save cost, IBM implemented the decoding of the wireless signal in software. They have connected the wireless receiver to the Non-Maskable Interrupt, the highest-level interrupt in the system.
But that brings us to the next incompatibility that IBM introduced: on the PC, XT and PCjr, they put the NMI control register at port A0h. On the AT however, they moved the NMI control to port 70h, as part of the new MC146818 CMOS configuration chip. What’s worse though, is that they put the second 8259A PIC at addresses A0h and A1h, so exactly where the old NMI control register used to be. On a regular PC or XT it is not that big of a deal, NMI is only used to report parity errors. The PCjr however uses it all the time, since it relies on it for the keyboard.
Oh, and a last annoying re-use of IBM: the PCjr’s enhanced graphics chip is known as the Video Gate Array, or ‘VGA’. Yes, they re-used ‘VGA’ later, for the successor of the Enhanced Graphics Adapter (‘EGA’), the Video Graphics Array.
What caused me headaches however, is a cost-saving feature that was common back in the day: incomplete decoding of address lines. By not connecting all address lines, the same device is actually ‘mirrored’ at multiple ports. For example, the SN76496 is not just present at C0h, but since it ignores address lines A0, A1 and A2, it is present at C0h-C7h.
The same goes for the NMI register: it is not present only at A0h, but through A0h-A7h. So guess what happened when I ran my code to detect a second PIC at address A1h? Indeed, the write to A1h would also go to the NMI register, accidentally turning it off, and killing my keyboard in the process.
It took me two days to debug why my program refused to respond to the keyboard, even though it was apparent that the interrupt controller was successfully operating in auto-EOI mode. Namely, the PCjr has a vertical blank interrupt, and I wanted to experiment with this. I could clearly see that the interrupt fired at every frame, so I had not locked up the interrupt controller or the system.
While tracking down the bug, I also discussed with Reenigne. Once I started suspecting that the NMI register is not just at A0h, but is actually mirrored over a wider range, I started to worry that the PC and XT may have the same problem. He told me that it is actually even worse on the original PC and XT. They are even more sloppy in decoding address lines (ignoring A0-A4), so the NMI register is present all through A0h-BFh.
In the end I had to make my detection routine for auto-EOI more robust. I already tried to use BIOS int 15h function C0h first, to get the information, but that fails on older systems such as the PCjr, since it was not introduced until 1986. This is why my PCjr got into the fallback code that tries to poll A1h to see if it responds like a PIC. I have added an extra level of safety now: If the int 15h function is not supported, I will first try to examine the machine model byte, located in the BIOS at addresss F000:FFFEh. This should at least allow me to filter out original IBM PCs, XTs and PCjrs, as well as clones that report the same byte. It may still not be 100% though.
This might be a good point to mention a similar issue I encountered some weeks earlier. Namely, I have a machine with an IBM Music Feature Card. Recently, I built a Game Blaster clone, designed by veovis. When I put it in the same machine, the IMFC started acting up.
What is the problem here? Well, the IMFC is configured to a base address of 2A20h. The first time I saw this, it already struck me as odd. That is, most hardware is limited to a range of 200h-3FFh (range 0-1FFh is originally documented as ‘reserved’, although the Tandy Clone card proves that at least writing to C0h works on the ISA bus). But, the IO range of an 8086 is 16-bit, so indeed any address from 0-FFFFh is valid. There is no reason to limit yourself to 3FFh, aside from saving some cost on the address decoding circuitry.
The problem is that the Game Blaster clone only decodes the lower 10 address bits (A0-A9). I configured it to the default address of 220h, which would be mirrored at all higher addresses of the form x220h (xxxxxx1000100000b). And indeed, that also includes 2A20h (0010101000100000b).
Now, was this a flaw in veovis’ design? Not at all. He made a clone of the Game Blaster, and the original Game Blaster does exactly the same thing, as do many other cards of that era (including IBM’s own joystick adapter for example). In fact, many later Sound Blasters still do this. So, this is a bit of a shame. Using a Game Blaster or Sound Blaster at the default base address of 220h will conflict with using an IMFC at its default base address of 2A20h.