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 8237 DMA controller: early PCs had only one 8237, 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.
Thoughts on Intel’s Meltdown issue?
Pretty sure the 8088 is not affected? 🙂
Pingback: What makes the PCjr cool, and what makes it uncool? | Scali's OpenBlog™
The “overeager” address decoding is a very poorly understood area of PC compatibility. Ignoring the lower address bits was partially documented (e.g. the 1983 XT Tech Ref lists the NMI Mask Register at address 0AX, not just 0A0, but does not document the multiply-decoded PIC and other registers). It can be also found in various “modern” 1990s chipset datasheets which at least sometimes document the behavior; for example the Intel PIIX datasheet (290550-001) has a nice table on page 25 showing exactly which lower address bits are ignored for various system devices. Unfortunately only people already aware of this behavior will make sense of the table.
Ignoring the higher address bits (like the Game Blaster does) is much less well documented and much more of a problem, because it’s prone to causing nasty addressing conflicts. I believe it comes from the fact that old IBM machines only decoded the low 10 bits of the I/O address when accessing system devices in the 00-FF range, and IBM typically did not list addresses above 3FF in the I/O address map.
The Mar ’84 PC/AT Tech Ref explicitly states on page 1-15 that the “I/O channel supports I/O address space hex 100 to hex 3FF”, and again on page 1-28 says that I/O addresses “hex 100 to 3FF are available on the I/O channel”. As far as I know, that’s somewhere between highly misleading and a bald-faced lie, because the full 16-bit address is visible on the ISA bus.
Even more intriguing is for example the Channel I/O Address Map on page 1-25 of the Mar ’86 IBM PC/XT and Portable Tech Ref. The text again says that addresses 100-3FF are available on the I/O channel, yet lists a number of IBM adapters using addresses well above 3FF. So there is a clear contradiction.
The way it actually works (to the best of my knowledge) is that aliases of the 00-FF range using 10-bit decoding (400-4FF, 800-8FF and so on) may be claimed by the system board and never make it out to the bus, but all other 16-bit addresses are visible. However, ISA cards (and if you’re unlucky, also MCA or other cards) may ignore the higher address bits and thus create aliases of themselves in the I/O space. The Game Blaster and Sound Blaster do that. Good luck finding it documented.
The way IBM clearly did things was to reserve an address range in the 100-3FF area and then use aliases higher up in the address space for related functionality. For example the primary IBM GPIB adapter used address 2E1, but further adapters could be at 42E1, 62E1, 82E1, and so on. Similarly the 8514/A used that approach to get lots of I/O addresses for a single adapter without requiring a large range in the 100-3FF area (even though IBM never released an ISA version of 8514/A so it was kind of pointless). Creative itself used the 620 range (an alias of the 220 range) for the AWE.
It’s a big mess.
Yes, at the very least we know that IBM officially lists the 00-FF range as ‘reserved’. When we were developing the Tandy Sound Card, we had our questions regarding this: the SN76489 chip on a real PCjr and early Tandys is at C0h, so right in that reserved area. Would that even work on an ISA slot?
As we found out, it did… at least, on IBM 5150, 5155 and 5160 models. The card works fine at C0h. It also works fine in various clones, even 286 and newer models (even though C0h is technically conflicting with the second DMA controller on AT-compatibles). I don’t think anyone ever tested on a 5162 or 5170 though.
So, at least writing to ports in the 00-FF range seems to work on IBMs and various clones. We are not sure if reading would also work, but for the Tandy card it’s no concern, since the SN76489 is a write-only device. The PCjr manual actually warns that trying to read from C0h may lock up the system.
I wouldn’t actually be surprised if reading would work… I suppose the main thing is that because of the sloppy address decoding, pretty much all of the 00-FF range is already taken by the motherboard. So that’s one reason you wouldn’t want to base your ISA cards there.
Yes, writes are always broadcast to everyone on the bus, even for system ports. There are a few devices which rely on that — sound cards which intercept/emulate the PC beeper, many PCI sound cards which snoop on DMA controller writes, diagnostic cards which snoop on port 80h writes.
For reads, I assume the system board will see the writes and respond, but another device may do the same. Which is almost guaranteed to end in tears, and the reason why ISA cards using the 00-FF range aren’t a good idea.
I can’t find the blog post, but one of you guys (either you or reenigne) was having a mixed relationship with the PCjr’s keyboard. Turns out there’s actually a pretty good replacement for it that you can still get to this day;
Wow, that’s quite a nice and high-end keyboard for the PCjr! I also like that they made it wireless, and added support for the regular PC as well. Probably extremely rare though, and impossible to get hold of 🙂
Pingback: The myth of the vertical retrace interrupt | Scali's OpenBlog™
Pingback: The myth of the vertical retrace interrupt #IBMPC #Graphics #VintageComputing « Adafruit Industries – Makers, hackers, artists, designers and engineers!
Pingback: The myth of the vertical retrace interrupt #IBMPC #Graphics #VintageComputing – Radio News Music Blog
Pingback: When is a PC not a PC? The PC-98 | Scali's OpenBlog™