Learn Something Old Every Day, Part XXI: VGA Memory Access Is Complicated

Last week I had another run-in with VGA emulation, and like last time, the cause was very likely the generally woefully inadequate VGA documentation.

The VGA is not, all things considered, a particularly complicated piece of hardware. It has no microcode, no CPU/microcontroller, and performs relatively simple functions. However, the VGA does consist of several logically separate components that work together, and this creates a perhaps underappreciated level of complexity.

By far the biggest difficulty in implementing VGA hardware or emulation is the lack of detailed and accurate documentation. The problem really started with the IBM EGA, because the VGA is only a slightly extended superset of the EGA.

The IBM EGA (Enhanced Graphics Adapter) was designed to support high-resolution (by 1984 standards) graphics in up to 16 colors, programmable fonts, but also with a high degree of compatibility with MDA/CGA text as well as CGA graphics modes.

However, the EGA is not register compatible with the earlier IBM adapters (and its internal architecture is quite different). Using code that directly sets up a CGA graphics mode (for example) will not work on an EGA. What does work is using the BIOS to establish the mode and then accessing video memory as if it were a CGA.

At the same time, the EGA is old enough that it was not a single integrated chip. It was built from four core chips: Graphics Controller, Sequencer, Attribute Controller, and CRT Controller. Each of these chips had its own set of registers.

The difficulty with EGA/VGA documentation is that the vast majority of it (starting with IBM’s own Technical References) is written for someone using a VGA, not implementing VGA functionality. And it is written with the assumption that the user is primarily interested in standard modes supported by the BIOS.

Most VGA references then say things like “for text modes, set bit X to 1 and bit Y to 0”. Which is useful enough if one wants to program text modes, but it does not at all explain what the bits actually do. Because the EGA consisted of several chips, it is common that bits in two or three chips have to be set certain way to achieve, say, CGA text mode compatibility or CGA graphics compatibility. Obviously each bit does something different, but users are “supposed” to set them all together. At the same time nothing actually stops programmers from flipping one or two bits out of a set of three, and then what happens? Some of the “undocumented” combinations turn out to be quite useful, such as the famous VGA Mode X.

The problem I tried to solve was related to the Odd/Even control registers in the VGA (or EGA, really) Sequencer and Graphics controller. Odd/Even access is used for text modes, and the name references the fact that the character codes are stored in even bytes and attributes in odd bytes, although really they’re stored in separate planes of the EGA/VGA four-plane memory organization.

IBM…

To compare, let’s see what the IBM VGA Technical Reference Manual (TRM) says about these bits:

  • SR4[2], bit OE: When the Odd/Even field (bit 2) is set to 0, even system addresses access maps 0 and 2, while odd system addresses access maps 1 and 3. When set to 1, system addresses sequentially access data within a bit map, and the maps are accessed according to the value in the Map Mask register (hex 02).
  • GR5[4], bit OE: When set to 1, the Odd/Even field (bit 4) selects the odd/even addressing mode used by the IBM Color/Graphics Monitor Adapter. Normally, the value here follows the value of Memory Mode register bit 2 in the sequencer.
  • GR6[1], bit OE: When set to 1, the Odd/Even field (bit 1) directs the system address bit, A0, to be replaced by a higher-order bit. The odd map is then selected when A0 is 1, and the even map when A0 is 0.

These three bits are all called ‘OE’ in the documentation. Note how the GR5[4] description does not really say anything, except that the GR5[4] value should “follow” the SR4[2] value. The GR6[1] documentation says what happens when the bit is set to 1, but does not explicitly say anything about setting the bit to 0.

From these descriptions, it’s next to impossible to divine what those bits do individually, just that they all need to be set a certain way to emulate CGA text or graphics modes.

Most VGA programming books and chip datasheets are similar to IBM’s documentation. The description is not wrong but it is rather inadequate. Fortunately, not all references are like that.

… vs Compaq

An unexpected source of helpful information turned out to be Compaq’s EGA TRG (Technical Reference Guide).

Here’s how Compaq documented the same bits (in the EGA, but they work the same on the VGA):

  • SR4[2]:
    0 = Even-numbered CPU addresses access planes 0 and 2. Odd-numbered CPU addresses access planes 1 and 3.
    1 = CPU addresses access data sequentially in the planes
    Bit <2> (odd/even bit) controls ONLY the CPU write accesses. CPU read accesses are controlled by the odd/even bit in the Graphics Controller Mode register.
  • GR5[4]: Odd/even bit
    0 = CPU reads data sequentially from the planes
    1 = Even CPU addresses access planes 0 and 2. Odd CPU addresses access planes 1 and 3
  • GR6[1]:
    0 = CPU address A0 is used as video memory address bit <0>
    1 = CPU address A0 is replaced by a higher CPU bit or the page select bit (bit <5>) from the Control and Status Miscellaneous Output register

In addition, register GR4 (Graphics Controller Read Plane Select) documentation has a handy table showing exactly how the GR4 register and the GR5[4] bit interact to determine read plane selection.

Compaq’s documentation is superficially similar to IBM’s but explains vastly more. It clearly states that SR4[2] controls CPU writes while GR5[4] controls reads. The bits have distinct and separate functionality, even if they are usually programmed in tandem.

Compaq also explains that while SR4[2] and GR5[4] control plane selection for writes and reads, respectively, bit GR6[1] controls addressing. In other words, GR6[1] determines which memory address is accessed, and SR4[2] with GR5[4] control which of the four planes at that memory address will be accessed, but separately for writes and reads.

As one might suspect, the three separate bits do in fact have separate functionality—which is hardly surprising, because if they didn’t, IBM wouldn’t need three of them. As an aside, these bits only control how VGA memory is read and written by the host CPU. There’s another, completely separate set of bits in the CRT Controller (CRTC) which determines how the data in VGA memory is displayed on the screen.

Honestly, thank you Compaq for taking the time to explain what the bits really do, instead of just vague hand-waving about how they’re meant to be used.

Questions Remain

Even the Compaq documentation is not complete. The GR6[1] documentation states that address bit A0 (coming from the CPU) “is replaced by a higher CPU bit or the page select bit”. But it doesn’t say which higher CPU [address, presumably] bit, or how the hardware decides whether to use a CPU address bit or the MSR[5] bit.

I found the answer in the Matrox MGA-1064 specification (when it comes to VGA registers, it’s very similar to MGA-2164, Matrox G100, G200, and similar documents). Matrox describes the GR6[1] bit as follows:

  • Odd/Even chain enable. VGA.
    0: The A0 signal of the memory address bus is used during system memory
    addressing.
    1: Allows A0 to be replaced by either the A16 signal of the system address (if memmapsl is ‘00’), or by the hpgoddev (MISC<5>, odd/even page select) field).

Well that is interesting. The memmapsl bits are GR6[3:2] and setting them to 00 means that the VGA decodes the entire A0000h-BFFFFh range. This setting is very unusual and in practice not used. But it actually makes sense.

The VGA (and EGA) has (or can be upgraded to, in case of EGA) 256K memory. However, due to the way it’s organized as four separate planes, there are only 64K unique addresses, each address with four planes behind it (eight bits per address on each of the four planes). When Odd/Even addressing is enabled, only every other address in VGA’s memory is accessed, because the A0 bit is fixed (that is, it does not come from the CPU). When the GR6[3:2] bits are programmed to 00, a 128K range can be used to access 64K unique addresses… which at first glance doesn’t make sense—except setting GR6[1] to one allows the user to access all even addresses in the 64K range through A0000h-AFFFFh and all odd addresses through the B0000h-BFFFFh range. And that is accomplished precisely by replacing the A0 bit coming from the CPU (which usually controls the odd/even plane select in text modes) by A16.

When the GR6[3:2] bits are set to a value other than 00 (decoding either 64K or 32K of address space), MSR[5] becomes the “page select” bit instead, allowing the user to choose which 64K are accessed.

The MSR[5] bit is remarkably poorly documented and different sources disagree on its polarity. Some (e.g. Matrox) claim that setting MSR[5] bit to 1 selects the “high page”, some (e.g. ATI 264VT, Cirrus Logic Alpine) claim that 1 selects the low page. Everything is further confused by the fact that the MSR[5] bit defaults to 0 after hardware reset, but just about every EGA/VGA BIOS sets it to 1.

Needless to say, there are also completely separate CRTC control bits which govern how data in VGA memory gets to be displayed. Bits CRTC14[6], CRTC17[0], CRTC17[1], CRTC17[5] and CRTC17[6] control how the CRTC addresses memory when it refreshes the display. The Matrox documentation of CRTC17 includes three handy tables showing how the CRTC bits transform the CRTC counter (16-bits, A0-A15) into memory address bits (MA0-MA15) in rather interesting ways.

The CRTC’s word access mode was clearly designed as a counterpart to the Odd/Even CPU addressing—although of course nothing forces programmers to use the two together. In word access mode, CRTC address bit A15 (assuming address wrapping is not forced by setting CR17[5] to zero) becomes memory address bit MA0. This mechanism lets the CRTC display even addresses in VGA memory when the CRTC address is in the 0000h-7FFFh range and odd addresses in the 8000h-FFFFh range. That neatly maps to the functionality provided by GR6[1].

I am not aware of any software actually using MSR[5] or the 128K VGA address range to increase the memory capacity of EGA/VGA text modes. Then again, the bits are there, and it’s almost guaranteed that some enterprising programmer used them.

Whether all of these bits even work on common VGA implementations is yet another question. The incomplete documentation likely prevented programmers from getting creative, which in turn hid problems caused by incomplete or inaccurate VGA implementations.

Why It’s Complicated

The above paragraphs illustrate why the VGA (and EGA) is complicated. There isn’t a single “Enable Text Mode” bit. There’s one control for CPU memory writes, another for CPU memory reads, another for CPU memory addressing. And then there’s a whole another set of bits that control the CRTC operation.

Under typical circumstances, these bits are programmed as a unit during a BIOS mode set. Yet all of these bits can be toggled individually, which creates a fairly large number of “undocumented” combinations. Because these combinations are not thought to be useful, the vast majority of VGA reference material just glosses over the details.

The catch is that programmers can and do tweak the individual bits, which means that in order to achieve a 100% (or at least very high) degree of compatibility, VGA hardware or emulation needs to correctly implement the behavior of every single bit. But without adequate documentation, that’s not a trivial task.

This entry was posted in Documentation, LSOED, VGA. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.