The IBM Color Graphics Adapter
The IBM Color Graphics Adapter (CGA) was one of the first video adapters available for the IBM PC, along with the IBM Monochrome Display Adapter and the third-party Hercules video adapter. It is perhaps the classic video card that comes to mind when people think of the IBM PC.
At a Glance
| Item | Description |
|---|---|
| Video memory | 16KiB at B800:0000; mirrored at BC00:0000 |
| Expansion ROM | None |
| Font ROM | 8KiB character generator ROM |
| Main display outputs | TTL RGBI on DE-9; NTSC composite video jack |
| Typical text modes | 40x25 and 80x25, 16 colors |
| Standard graphics modes | 320x200, 4 colors; 640x200, 2 colors |
| I/O address range | 3D0h-3DFh |
| CRTC address port | 3D4h standard, 3D0h, 3D2h, 3D6h alternate* |
| CRTC data port | 3D5h standard, 3D1h, 3D3h, 3D7h alternate* |
| CGA control ports | 3D8h mode control, 3D9h color control |
| CGA status port | 3DAh |
| Light pen latch ports | 3DBh clear latch, 3DCh preset latch |
| Interrupts | None |
| DMA | None |
The CGA could be connected to a regular North American television set via its composite output connector, although most sets of the time would require an RF modulator to do so. A DE-9 connector provided a digital RGBI signal that could be used with an IBM 5153 Color Display. IBM left owners of the CGA waiting a bit for that particular monitor - it was only released in 1983, two years after the CGA’s debut.
The CGA has 16KiB of DRAM dedicated to video memory, and an 8KiB font ROM that holds bit patterns for drawing text glyphs.
The CGA has no on-board video BIOS or expansion ROM. All PC-compatible BIOS implementations must therefore know how to identify, initialize and operate a CGA card in order to provide standard int 10h services.
In text mode, the CGA card was capable of outputting 16 colors. In graphics mode, it was limited to 3 palettes of 3 fixed colors each, with a selectable background color. The CGA also had a high-resolution mode, with a single, selectable foreground color on black.
Like the MDA, the CGA is built around the Motorola MC6845 CRTC. See that chapter first for a basic understanding of how that chip is used to define display geometry.
The CGA card has 16KiB of RAM at segment B800:0. This is decoded twice through the range B800:0-C000:0, causing a mirror of video memory that begins at BC00:0. You can use this mirroring to your advantage in certain circumstances.
The CGA maps the MC6845’s two external registers at address 03D4h for the address register and 03D5h for the data register.
* Incomplete decoding of the CRTC registers means the two CRTC ports are repeated four times. At least one known game, Prohibition, relies on this incomplete decoding.
Operational Modes
The CGA has three primary modes of operation: text mode, medium-resolution graphics mode, and high-resolution graphics mode. A brief overview of each mode follows; each will be covered in greater depth later on.
Text Mode
In text mode, the screen is constructed from a grid of character glyphs. IBM called this mode Alphanumeric Mode (A/N) in documentation.
The IBM PC boots into text mode, either in 40-column or 80-column mode depending on how certain DIP switches are set. 40-column mode makes each glyph twice as wide due to a halved dot clock. It was primarily intended for use with television sets, on which 80-column text was difficult to read. Various tweaked text modes were derived by inventive coders over the years that provided a different number of rows and columns, so these extents are not set in stone.
The standard text modes have black and white and color mode variants, although this typically only controls color when using the composite output. The background and foreground color of each character cell can be controlled via attribute bytes.
In text mode, a blinking, hardware cursor is usually shown indicating where the user can type - or if the programmer doesn’t bother hiding it, where the screen was last updated.
CGA 80-column text mode - MS-DOS 5.0 EDIT
Medium Resolution Graphics Mode
In medium-resolution graphics mode, the screen is composed of a 320x200 grid of pixels. IBM called graphics mode All-Points-Addressable (APA) mode.
This is perhaps the most famous mode of the CGA, especially its cyan-magenta-white palette as seen in games like Alley Cat.
In this mode, three basic ‘palettes’ are available, two of which are notorious for being ugly. These are not actually palettes as they are typically understood - more on that below. Index 0, the background color, is usually black, but can be chosen from any of the CGA’s 16 possible colors. Careful use of this ‘extra’ palette entry can create interesting effects, that also extend into the borders or overscan of the image. Games could use this color to indicate status, such as flashing it red to indicate your player character had taken damage.
The cursor is disabled in this mode.
CGA 320x200 graphics mode - Alley Cat by Bill Williams
High Resolution Graphics Mode
This mode provides a very stretched 640x200 monochromatic graphics mode. The foreground color used to draw can be selected from any of the 16 available CGA colors, but the background cannot be changed from black. This mode can be used, with the color burst turned on, to enable 16-color composite artifact color modes.
The cursor is disabled in this mode.
CGA 640x200 graphics mode - SHUTTLE.BAS
The RGBI Color Gamut
The CGA has a digital DE-9 output connector. To produce color, the CGA controls four output pins, one for each of the primary colors: Red, Green, Blue, and an Intensity signal. Four bits give us \(2^4\) or 16 possible colors. The intensity pin provides a repeat of the first eight colors, but brighter.
The odd duck out here is non-intensified yellow, which has been conspicuously replaced with brown. This was a deliberate decision by IBM, who perhaps found the rather sickly dim yellow unpleasant, or at least believed that a brown color would be more useful. Speculation abounds, but the interesting thing to note is that the conversion circuit to turn yellow into brown is not actually present on the CGA at all - the circuitry is within the IBM 5153 Color Display itself, and third party PC-compatible monitor manufacturers largely followed suit. You may see the original yellow color on RGBI monitors not intended to be CGA compatible, such as the Commodore 1084 series.
| Decimal | R | G | B | I | Color | Hex |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 000000 | |
| 1 | 0 | 0 | 1 | 0 | 0000AA | |
| 2 | 0 | 1 | 0 | 0 | 00AA00 | |
| 3 | 0 | 1 | 1 | 0 | 00AAAA | |
| 4 | 1 | 0 | 0 | 0 | AA0000 | |
| 5 | 1 | 0 | 1 | 0 | AA00AA | |
| 6 | 1 | 1 | 0 | 0 | AA5500 | |
| 7 | 1 | 1 | 1 | 0 | AAAAAA | |
| 8 | 0 | 0 | 0 | 1 | 555555 | |
| 9 | 0 | 0 | 1 | 1 | 5555FF | |
| 10 | 0 | 1 | 0 | 1 | 55FF55 | |
| 11 | 0 | 1 | 1 | 1 | 55FFFF | |
| 12 | 1 | 0 | 0 | 1 | FF5555 | |
| 13 | 1 | 0 | 1 | 1 | FF55FF | |
| 14 | 1 | 1 | 0 | 1 | FFFF55 | |
| 15 | 1 | 1 | 1 | 1 | FFFFFF |
In text mode, any of these sixteen colors can be referenced within a character attribute byte.
In graphics modes, the CGA operates in what we might term direct color mode, where the bits set in video memory directly influence the Red and Green output pins.
The CGA only has one true palette register as we typically define one, as in a register that holds an arbitrary color. It is the background/overscan color field in the Color Control Register.
The CGA Registers
Mode Control Register
| Bits | Name | Description |
|---|---|---|
| 0 | HIRES | 80-Column Text Mode Timings Enabled: 0: Normal Timings 1: 80-column Timings |
| 1 | GFX | Display Mode: 0: Text 1: Graphics |
| 2 | B/W | Black-and-White Mode 0: Enable color burst 1: On composite output, disables the color burst. In medium-resolution graphics mode, selects the alternate red/cyan/white palette. |
| 3 | VIDEO | Enable Video Serializers 0: Video Serializers Disabled 1: Video Serializers Enabled |
| 4 | HIRES_GFX | High-Resolution Graphics MUX Control: 0: Disabled 1: High-Resolution Graphics Mode Enabled |
| 5 | BLINK_EN | Text Blinking Control 0: Attribute bit 7 controls background intensity1: Attribute bit 7 controls blinking |
| 6:7 | Unused | Unused |
The CGA Mode Register generates the main control signals that drive the logic of the card. Most descriptions of what these bits do, even in IBM’s own references, only give you an approximation of their actual function. To enter a standard graphics mode, besides reprogramming the CRTC, the correct mode bits must be set.
- The HIRES bit enables the 14.31818MHz dot clock and high-resolution character clock for 80-column text mode. It should not be used in conjunction with the GFX or HIRES_GFX bits.
- The GFX bit enables graphics mode, and triggers replacement of memory address
A13withRA0; a further explanation is given in the medium-resolution graphics section. - The B/W bit disables the composite color burst. It will produce a black and white or grayscale image on a composite monitor or television set. RGBI displays such as the IBM 5153 Color Display will still display color as normal with this bit set, although a third graphics palette can be selected via this bit in medium-resolution graphics mode.
- The VIDEO bit is described by IBM as disabling the video signal - this is not accurate. This bit pulls low the \(\overline{\mathrm{CLR}}\) pins of the CGA’s graphics serializers, U7 and U8. The effect of this is the appearance that all video memory contains
0. This often does result in a black screen, but where conditions allow display of color, such as the border/overscan color being set, color will still be displayed. - The HIRES_GFX bit enables logic on the card to rapidly enable and disable the CGA’s color multiplexers. This bit should not be used in conjunction with HIRES or the text on the screen will become corrupted.
There are several combinations of mode bits that are invalid, and may have strange and interesting effects.
| Mode Bits | Display Mode | Effect |
|---|---|---|
00100 | Mode 0 | 40-Column, B/W Text Mode |
00000 | Mode 1 | 40-Column, Color Text Mode |
00101 | Mode 2 | 80-Column, B/W Text Mode |
00001 | Mode 3 | 80-Column, Color Text Mode |
00011 | invalid | Glitched mode: “Text and Graphics” |
00010 | Mode 4 | 320x200 Graphics Mode |
00110 | Mode 5 | 320x200 Graphics Mode (Alternate Palette) |
10110 | Mode 6 | 640x200 Graphics Mode |
10001 | Invalid | Glitched mode: Text Mode with black bars |
| Bits | Name | Description |
|---|---|---|
| 0 | B | Blue component of the border/background color |
| 1 | G | Green component of the border/background color |
| 2 | R | Red component of the border/background color |
| 3 | I | Intensity component of the border/background color |
| 4 | PAL_I | Drives the Intensity signal in graphics mode, effectively creating a brighter version of the current palette. |
| 5 | PAL_B | Drives the Blue signal in graphics mode, effectively creating a new palette. |
| 6:7 | Unused | Unused |
The color control (or color select) register contains the CGA’s only real color palette entry - an RGBI color may be specified that provides the background/overscan color. This color definition has three distinct use cases - in text mode, it provides the border/overscan color. In medium-resolution graphics mode, it provides the color to use when a pair of bits are both 0, in addition to providing the border/overscan color. In high-resolution graphics mode, it controls the color used to represent 1 pixels. Note that I have deliberately avoided calling this the “foreground color”. See the section on high-resolution graphics mode for the reason why.
The overscan or border is an infamously large area around the visible or active display area of the IBM CGA. Here is a fairly accurate representation of its extents on an IBM 5153 monitor. If you look closely, you can even see the command that has just set the overscan color to cyan:
The CGA overscan/border region, set to cyan via the CC register (Click to Zoom)
The other two bits in the color control register, PAL_I and PAL_B, provide the intensity and blue components of all colors generated in medium-resolution graphics mode. They effectively select from four different color gamuts.
| Bits | Name | Description |
|---|---|---|
| 0 | DE | Inverted display-enable signal from CRTC. 0: CRTC is scanning inside the active display area. 1: CRTC is scanning outside of the active display area. |
| 1 | LPT | Light-pen trigger state 0: Light pen trigger has not fired 1: Light pen trigger has fired. |
| 2 | LPS | Light-pen switch status 0: Light pen switch on 1: Light pen switch off. |
| 3 | VR | Vertical retrace status 0: VS pin of CRTC is low 1: VS pin of CRTC is high (in vertical blank) |
| 4:7 | Unused | Unused |
The CGA status register contains two essential bits for synchronizing graphics with the display. Bit 0, \(\overline{\mathrm{DE}}\), is the inverted DE pin from MC6845. Therefore it will be 0 when the MC6845 is scanning across the active display area, when the native DE pin is 1.
Bit 3, VR, is the non-inverted VS pin from MC6845 latched on the next hclock. When VR is 1 we are in the MC6845’s vertical blanking period. Note that, unless some serious CRTC abuse is occurring, \(\overline{\mathrm{DE}}\) will always be 1 when VR is 1.
The CGA’s memory access and rasterization latency produces one character of display enable skew. Therefore, \(\overline{\mathrm{DE}}\) will flip to 0 one character clock before the CGA starts drawing the active display area, and will flip back to 1 one character clock before the end of the active display area is drawn.
The following diagram may help clarify the values of these two status bits at different points on the screen:
A visualization of the /DE and VR bits in the CGA Status Register (Click to Zoom)
The two other bits in the CGA status register concern themselves with light pen operation.
-
The LPT bit is the state of the light pen strobe trigger. The term trigger can evoke the sense of a physical trigger or button on the light pen, but this is not the case. The light pen trigger is fired when the photodiode within the attached light pen detects light (the strobe) from the CRT raster beam. When this bit is
1, the MC6845’s light pen latch registers should contain the valid approximate position of the light pen. This bit is latched via a flip-flop on the CGA, and will remain set indefinitely unless cleared by writing any value to port3DBh, after which a new strobe trigger may fire and a new light-pen position latched by the MC6845. The strobe trigger may be manually fired without input from the pen by writing to port3DCh. It is possible to crudely determine the current raster position of the display with this method, but it is highly unreliable. Even so, it didn’t stop games like Jungle Hunt from using it to perform mid-frame palette swaps. -
The LPS bit is the immediate state of any switch connected to the light pen header’s switch pin. The light pen switch signal is active-low, explaining why this bit is logically reversed. This is simply the immediate state of any switch present on the light pen - this switch may be at the tip of the pen on high-quality light pens. The switch is typically used for taking actions such as initiating a drawing operation. IBM’s documentation warns us that this signal is not debounced in any way, but a high quality pen should debounce the switch for us.
Text Mode
In text mode, video memory is organized conceptually as a grid of character cells. The dimensions of this grid are directly configured on the CRTC, and are typically 80x25. Each logical cell consists of a pair of bytes in video memory, the first byte being a character code and the second byte being a character attribute.
The character code, combined with the vertical line counter of the CRTC, is used to resolve a byte contained in the CGA’s font ROM representing 8 pixels (or span) of a character glyph. You can see the standard CGA character glyphs below.
CGA Character Glyphs (Standard Font)
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ||||||||||||||||
| 1 | ||||||||||||||||
| 2 | ||||||||||||||||
| 3 | ||||||||||||||||
| 4 | ||||||||||||||||
| 5 | ||||||||||||||||
| 6 | ||||||||||||||||
| 7 | ||||||||||||||||
| 8 | ||||||||||||||||
| 9 | ||||||||||||||||
| A | ||||||||||||||||
| B | ||||||||||||||||
| C | ||||||||||||||||
| D | ||||||||||||||||
| E | ||||||||||||||||
| F |
The standard CGA font implements IBM Code Page 437. The vast majority of CGA cards were shipped with this font ROM, however there were variants sold for the international market that contained alternate code pages.1
The CGA ostensibly provides a second “thin” font selectable by a jumper, but on most examples of the IBM CGA this jumper is not populated on the PCB, requiring some soldering to actually utilize. Why IBM changed their mind about making this font user-selectable is up for debate. However, an emulator author can certainly choose to make the font more easily selectable.
A visualization of the character font ROM is shown below, with bytes reversed and wrapping vertically to fit into a square image. The first half of the ROM is dedicated to the MDA’s 8x14 font, each glyph of which is split into two parts. The latter half of the ROM consists of the ‘hidden’ thin font, followed by the standard CGA font.
The MDA/CGA character font ROM (byte-reversed)
The character font ROM is not accessible from the host PC. It can only be read by the CGA itself. int 10h services include routines for drawing text in graphics modes - to accomplish this, a copy of the standard CGA font is present in the IBM PC BIOS as well.
Character Attribute Byte
| Bits | Name | Description |
|---|---|---|
| 0 | FGB | Blue Foreground |
| 1 | FGG | Green Foreground |
| 2 | FGR | Red Foreground |
| 3 | FGI | Foreground Intensity |
| 4 | BGB | Blue Background |
| 5 | BGG | Green Background |
| 6 | BGR | Red Background |
| 7 | BGI / BL | Background Intensity/Blink |
The character attribute byte defines the foreground and background colors the CGA should use when rendering a glyph. The lower four bits provide the color to paint 1 bits which represent the foreground in the raw data from the font ROM. The upper three or four bits provide the color to paint 0 bits which represent the background. Bit 7 is a bit special - the CGA has an optional mode that enables text blinking. When this mode is enabled, bit 7 of the attribute byte controls whether the text blinks or not. This means that only three bits are now available for the background color, so backgrounds are limited to the first 8 “darker” shades when blinking is enabled. See the section on text blinking for more information.
CGA attribute byte visualization
Since each character cell has a character code and attribute byte, each character takes one 16-bit word of memory. Thus it takes 4KiB of memory to display a single 80x25 text mode screen. This means up to four text-mode screens can fit in the CGA’s 16KiB of memory, and a program can switch between each screen by adjusting the CRTC’s start address registers.
When multiple screens can be fit into video memory, they are called video pages. Switching between video pages is referred to as page-switching or page-flipping when used for fast animation, but there are many other uses - a help page could be stored separately from a text editor’s main interface, allowing the program to switch to the help screen and back without having to redraw either page.
Alternatively, a single large screen of up to 80x100 could be stored in memory and the visible 80x25 region panned or scrolled down through it by adjusting the start address registers one row at a time.
Cursor Blinking
The Motorola MC6845 CRTC provides a dedicated CURSOR pin and internally handles blinking at a rate of either 16 (8 frames on, 8 frames off) or 32 fields (16 frames on, 16 frames off). It is possible to configure the MC6845 to disable blinking entirely, which would normally leave a solid, static cursor, and indeed, this is how the IBM PC BIOS configures the MC6845 by default. The cursor blinks anyway, because the CGA card has its own independent blinking logic which is combined with the CURSOR signal from the MC6845.
The CGA’s blink logic is implemented as two 4-bit binary counters, tied to the VSYNC output of the MC6845 as seen below. The \(\overline{\mathrm{CURSOR\_BLINK}}\) signal controls the cursor blinking rate, and will always blink at a rate of 16 fields, equivalent to the normal blink rate of the MC6845. The \(\overline{\mathrm{BLINK}}\) signal controls text blinking and blinks at half the rate due to being output from the next bit of the binary counter chain.
CGA blink signal generation
If the MC6845 is configured for normal blink timings, the CGA’s blink logic will match it perfectly. If the ‘slow’ blink rate is selected, then the CGA’s blink logic and the MC6845’s blink logic will be out of phase; when these signals are effectively AND’d together, the cursor will blink off twice as long as it will blink on:
CGA and MC6845 blink coincidence visualized
Besides blinking, the cursor can be disabled a number of ways, or even ‘split’ into two sections. See the MC6845 chapter’s section on cursor tricks.
Text Blinking
Text blinking must first be globally enabled by setting bit 5 of the mode control register. When enabled, bit 7 of each character attribute byte determines whether that character cell will blink.
Blinking text blinks at half the rate of the cursor - 16 frames on, 16 frames off. When text blinks ‘off’, the background attribute is displayed for the entire character cell, allowing for blinking with reverse-video.
Blinking text on the IBM CGA
A subtle, easily-missed detail regarding text blinking on the CGA is that when the MC6845’s cursor signal is active, text blinking is disabled. This only affects the rows of a glyph within the cursor extents. Remember that the MC6845’s cursor is normally configured not to blink. This means that when the CGA’s own blinking disables the cursor, the rows of the glyph subsequently revealed will not blink along with the rest of the glyph.
If the MC6845’s cursor blinking is enabled at either rate, as you can imagine the intended behavior of non-blinking text will be disturbed as the no longer steady CURSOR signal will toggle on and off, periodically enabling and disabling text blinking in the cursor area.
Medium-Resolution Graphics Mode
In the CGA’s 320x200, 4-color graphics mode, pairs of bits (referred to as C0 and C1) are interpreted by the CGA’s color multiplexer more-or-less directly as red and green.
This is an important distinction - the CGA has no traditional graphics palettes as we typically understand them, other than the palette entry used for the background color (when both C0 and C1 are 0). Two bits of each color to be emitted by the CGA are provided from video memory, with the other two bits provided by the PAL_B and PAL_I bits of the color control register.
Medium-resolution graphics mode is enabled by setting the GFX bit in the mode control register.
With the color control register set to its default of 0, we are left with the traditional red-green-brown palette.
| Bytes from video memory are serialized as pairs of bits, C0 and C1. | |
| These bits drive the Red and Green channels. | |
| When both Red and Green are active, yellow is produced... | |
| ...but is converted to brown by the IBM 5153 monitor. |
You can easily see that each of the four traditional ‘palettes’ of the CGA have identical red and green components, differing only in the presence or absence of blue or intensity bits:
Primary Palette (Blue Disabled)
| R | G | B | I | Color | Hex |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | Overscan | |
| 0 | 1 | 0 | 0 | 00AA00 | |
| 1 | 0 | 0 | 0 | AA0000 | |
| 1 | 1 | 0 | 0 | AA5500 |
| R | G | B | I | Color | Hex |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 1 | Overscan | |
| 0 | 1 | 0 | 1 | 55FF55 | |
| 1 | 0 | 0 | 1 | FF5555 | |
| 1 | 1 | 0 | 1 | FFFF55 |
Secondary Palette (Blue Enabled)
| R | G | B | I | Color | Hex |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | Overscan | |
| 0 | 1 | 1 | 0 | 00AAAA | |
| 1 | 0 | 1 | 0 | AA00AA | |
| 1 | 1 | 1 | 0 | AAAAAA |
| R | G | B | I | Color | Hex |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 1 | Overscan | |
| 0 | 1 | 1 | 1 | 55FFFF | |
| 1 | 0 | 1 | 1 | FF55FF | |
| 1 | 1 | 1 | 1 | FFFFFF |
Alternate Palette (Conditional Blue)
There is a third, undocumented palette of cyan, red and white. Many find it to be the most aesthetically pleasing of all the CGA palettes. This particular palette was only implemented to provide better contrast when displaying medium-resolution graphics mode on a monochrome composite display, such as a black-and-white television set. It was not implemented for its color aesthetics, and thus IBM probably didn’t see fit to document it as a distinct palette option. After all, later revisions of the CGA could have always disabled the need for it by adjustments to the composite output circuitry.
The palette is enabled by setting the BW bit in the mode register. The color modification is produced via miscellaneous logic on the CGA that enables the blue video output unless C0 and C1 decode to 10 (red).
| R | G | B | I | Color | Hex |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | Overscan | |
| 0 | 1 | 1 | 0 | 00AAAA | |
| 1 | 0 | 0 | 0 | AA0000 | |
| 1 | 1 | 1 | 0 | AAAAAA |
| R | G | B | I | Color | Hex |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 1 | Overscan | |
| 0 | 1 | 1 | 1 | 55FFFF | |
| 1 | 0 | 0 | 1 | FF5555 | |
| 1 | 1 | 1 | 1 | FFFFFF |
The key point is that medium-resolution graphics mode bit-pairs do not form palette indices in the more typical sense. Instead, they directly form part of an RGBI signal that is completed by the bits in the color control register or alternate external logic.
Graphics Mode Function
If you have read the MC6845 chapter, you may be curious how a text-oriented display controller can be utilized to implement a graphics-oriented mode. The MC6845 still counts out word addresses on the CGA, and for each low-resolution character clock, two bytes are fetched and 8 pixels are emitted. The CGA utilizes the same pair of byte serializers that it uses to serialize the character glyph byte and character attribute byte in text mode, although the color multiplexer is set for input 2.
This is all fine for scanning out individual rows of graphics data, but one immediate issue is present: the vertical total register, vertical displayed register, and vertical character counters of the MC6845 are all only 7 bits. That limits us to a total of 128 logical rows. Medium-resolution graphics mode is 320x200. Clearly, 200 will not fit into 128, so some workaround is needed.
The 8KiB Interleave
IBM came up with a clever solution to this problem, but one that would make programming software for the CGA’s graphics mode fairly cumbersome. In standard graphics modes, rows are set to be two scanlines tall by configuring R9 as 1 (recall this defines the line height of a character row, minus 1). The number of displayed rows, R6, is then set to 100.
This creates 100 logical rows, each two scanlines tall, to give us the desired 200 total visible scanlines. The MC6845 will scan out each logical row twice, with the MC6845’s row counter pin 0, RA0, counting from 0 to 1 for the second scanline.
The memory address line A13 is then exchanged for RA0. This substitution is made when the GFX bit of the mode control is set.
This creates an 8KiB offset in memory for all odd scanlines. Since an extra row-counting bit has been added, we can now define up to 256 scanlines, easily fitting in our 200-line graphics mode, at the cost of an annoying interleaving scheme. Programmers could not simply copy bitmaps or sprites into the CGA’s video memory without taking this interleaving into account.
The CGA graphics mode 8KiB offset - video memory visualized side by side with gameplay of 'Digger'
High-Resolution Graphics Mode
This is a 640x200 monochromatic, 1bpp graphics mode. The CGA operates at the low-resolution character clock in this mode, despite the 640 columns of resolution. Each character clock, two bytes from video memory are fetched and 16 pixels are emitted. The color used for drawing 1 bits is the color defined in the color control register.
This is sometimes referred to as the foreground color in this mode, but it is actually still the background color. If that sounds paradoxical, it is explained by the way that this mode is implemented: in high-resolution graphics mode, the CGA’s color multiplexers are set to always draw the border/overscan color. The CGA then rapidly enables and disables the color multiplexers themselves. So in this mode, the foreground is actually the background, and the background is actually just “off.”
You can, of course, leave the color defined in the color control register set to 0, but you will not see anything drawn.
Like medium-resolution graphics mode, an 8KiB offset is created between odd and even scanlines via substitution of A13 with RA0.
High-resolution graphics mode is enabled by setting the GFX and HIRES_GFX bits in the mode control register. Interestingly, the HIRES_GFX bit controls the rapid toggling of the CGA’s color multiplexers, and it is possible to enable this mode at other times - such as in 80-column text mode, and watch as the CGA dutifully toggles the screen on and off as text mode is drawn, leading to a bizarre, striped appearance.
Low-Resolution “Graphics” Mode
Although not actually a graphics mode at all, some games on the PC effectively created a low-resolution graphics mode by setting up a tweaked text mode to display pixel art at an effective resolution of either 80x100 or 160x100.
It is important to differentiate this mode from BIOS video mode 08h which is a true 160x100 graphics mode available only on IBM PCjr and Tandy 1000 systems.
The trick of this mode is to fill video memory with either of two specific CGA character codes:
| Hex | Glyph |
|---|---|
| DD | |
| DE |
Each of these glyphs evenly divides the 8-pixel span of a character cell into half foreground and half background.
Next, the value of R9 on the CRTC is set to 1, making each logical row of character cells two scanlines tall.
Since we can independently control foreground and background colors via character attribute bytes, this essentially creates an all-points-addressable display, with each character cell forming two 4x2 pixel wide ‘pixels’. Updating the screen now involves writing only to the CGA attribute bytes.
Given the CGA’s drastically stretched aspect ratio, these end up displaying as square - as seen below in an aspect-corrected screenshot of the PC shareware title, Round 42:
Low-resolution "Graphics" mode in Round 42 by Mike Pooler
Of course, nothing strictly limits you to only using the two convenient ‘split-block’ characters. If we consider using the top two rows of the standard CGA font, many potentially useful patterns can be found:
The top two rows of the IBM CGA code page 437 character set
Several titles by the developer Macrocom utilized this technique, and so it became known in some circles as the “Macrocom Method”, or alternatively, “ANSI from Hell”2.
Here’s an example of how the player character ‘sprite’ in Macrocom’s ICON: Quest for the Ring is built up from standard character glyphs, as seen with varying values for the MC6845’s Maximum Scanline register, R9:
A visualization of the the ASCII-rendered player character in Macrocom's ICON: Quest for the Ring
In 2022, a demo for the PC was released that pushed this technique to its absolute limits: Area 5150. Through many clever additional CRTC tricks, it was able to make it nearly impossible to tell that any of the demo’s effects were actually running in text mode.
| R9==7 | R9==1 |
|---|---|
|
|
For a rather comprehensive review of the various titles that have used a tweaked text-mode over the years, see this excellent post on the blog Nerdly Pleasures.
Display Timings
Unlike the MDA, the CGA does not have its own crystal. IBM designed the main system crystal of the PC itself around the NTSC display standard, with the apparent intent of simplifying the production of the CGA and other television-compatible peripherals.
The 5150 has a single main system crystal with a frequency of 14.31818MHz. This frequency is exactly four times the NTSC color subcarrier frequency.
The crystal frequency can be expressed as a fraction:
$$f_{crystal} = \frac{315}{22} \text{ MHz} = 14.318181\overline{81} \text{ MHz}$$
The CGA’s output is almost but not quite entirely NTSC-conforming. A real NTSC signal provides two interlaced fields of 262.5 scanlines, whereas the CGA outputs 262 progressive scanlines at approximately 60fps. This 525 vs 524 scanline difference is minor enough for television sets to ignore.
The CGA produces a display field of \(912 \times 262\) or \(238,944\) hdots.
The exact vertical refresh rate of the CGA can be calculated as:
$$f_{refresh} = \frac{14{,}318{,}181}{238{,}944} = 59.92 \text{ Hz}$$
The horizontal retrace rate can be calculated as:
$$f_{hsync} = \frac{14{,}318{,}181}{912} = 15.70 \text{ kHz}$$
This is a significant number in that you will often hear monitors capable of displaying 200-line resolution modes produced by the CGA and EGA video cards described as 15kHZ displays.
Aspect Ratio
The CGA’s display field of 912x262 is quite far from square. The IBM 5153 Color Display has an aspect ratio of approximately 4:3, but this is hardly the final word on the matter - see this VCF forum thread for a surprisingly in-depth discussion of the topic.
The CGA’s aspect ratio is of primary importance when displaying circles - non-circular circles will be immediately obvious to the user. Many monitors had vertical and horizontal adjustment knobs, which a user could use to adjust their display if circles appeared more like ellipses; however, they might find that different adjustments were needed for different software applications that made different assumptions. An emulator author could do worse than to make the effective aspect ratio user-configurable.
A 4:3 aspect ratio applied to a 640-pixel wide image produces the familiar 640x480 resolution. The images in this chapter have all been aspect-corrected to 4:3.
CGA Clocks
The 14.31818MHz clock of the CGA can be used directly as the dot clock, which is the case in the CGA’s high-resolution 80-column text mode. Alternatively, it can be divided by two to produce a 7.159MHz dot clock, which is done in 40-column text mode, and for all of the CGA’s graphics modes. The effect of halving the dot clock effectively doubles the width of each pixel, as the card toggles its RGBI outputs at half the rate, while the monitor continues scanning out at the same rate as always.
The CGA further divides the dot clock by 8 to produce a character clock. In 80-column text mode, this clock runs at 1.79MHz and is called the high-resolution character clock, or hclock. In graphics mode, or 40-column text mode, the CGA accordingly uses the low-resolution character clock or lclock that runs at 895kHz.
When using the hclock, the CGA typically outputs 640 pixels per scanline. When using the lclock, the card typically outputs 320 pixels per scanline, as the effective width of each pixel is doubled since the raster beam continues to scan out the screen at the same rate. The CGA’s high resolution graphics mode is an exception to this, as it has a little trick up its sleeve - it draws by modulating the CGA’s color multiplexers on and off at a rate driven by the original 14MHz dot clock.
Since both the dot and character clocks have the same relationships, it can be useful to discuss the CGA’s effective clock divisor for a given mode. This is fairly easy: for 80-column text mode, the clock divisor is 1 or undivided - for all other modes it is 2.
With either clock, the number of vertical scanlines remains the same, but the horizontal timings programmed into the CRTC must be adjusted to account for the particular dot clock in use.
BIOS Video Modes
The PC BIOS maintains video services via the int 10h service. The first function available, 00, requests that a video mode be set. This led to several standard video mode definitions, the first seven of which, 00h-06h, are modes compatible with the CGA.
| Text / Graphics |
Size | Mono / Color / Grayscale |
Colors | Character Cell | |
|---|---|---|---|---|---|
| 00h | Text | 40x25 chars | Grayscale* | 16 shades | 8x8 |
| 01h | Text | 40x25 chars | Color | 16 colors | 8x8 |
| 02h | Text | 80x25 chars | Grayscale | 16 shades | 8x8 |
| 03h | Text | 80x25 chars | Color | 16 colors | 8x8 |
| 04h | Graphics | 320x200 | Color | 4 colors | 8x2 |
| 05h | Graphics | 320x200 | Grayscale | 4 shades | 8x2 |
| 06h | Graphics | 640x200 | Mono | 1 color | 16x2 |
* Grayscale video will only be displayed on a composite monitor or television set.
CRTC Parameters
For each video mode, the MC6845 CRTC needs to be configured with the correct parameters for registers R0—R9. The standard CRTC parameters are given below.
| Register | Name | Text Mode | Graphics Mode | |||
|---|---|---|---|---|---|---|
| 40 Column | 80 Column | "160x100" | 320x200 | 640x200 | ||
| R0 | HorizontalTotal | 56 | 113 | 113 | 56 | 56 |
| R1 | HorizontalDisplayed | 40 | 80 | 80 | 40 | 40 |
| R2 | HorizontalSyncPosition | 45 | 90 | 90 | 45 | 45 |
| R3 | SyncWidth | 10 | 10 | 10 | 10 | 10 |
| R4 | VerticalTotal | 31 | 31 | 127 | 127 | 127 |
| R5 | VerticalTotalAdjust | 6 | 6 | 6 | 6 | 6 |
| R6 | VerticalDisplayed | 25 | 25 | 100 | 100 | 100 |
| R7 | VerticalSync | 28 | 28 | 112 | 112 | 112 |
| R8 | InterlaceMode | 2 | 2 | 2 | 2 | 2 |
| R9 | MaximumScanLineAddress | 7 | 7 | 1 | 1 | 1 |
Video Memory
The 16KiB of DRAM on the CGA is not expandable. It is also single-ported, meaning that only either the CPU or the CGA can access the video memory at any given time. This is a bit of a problem as the CGA needs to be reading video memory constantly as it rasterizes the screen. We will cover those issues in the next section, Memory Contention
The 16KiB of video memory is composed of 8 separate 16Kbit DRAM chips, each of which provides a single bit. These bits are organized logically as a 128x128 grid - to address a single bit, a 7-bit column address must be provided followed by a 7-bit row address. The CGA produces these RAS and CAS addresses from the MA pins of the 6845 with some additional manipulation, such as substituting RA0 for A13 in graphics modes.
Memory Contention
The CGA has some circuitry for attempting to arbitrate access to memory between the CPU and the card itself (which IBM refers to, somewhat confusingly, as the CRT). This is done via manipulation of the IO_CH_READY pin of the ISA bus. When this pin is pulled low, any attempts by the CPU to access the CGA’s video memory will stall, with the CPU performing wait states until the pin is released. In this manner the card can guarantee exclusive access to video memory for itself - but only at the slower, 7MHz dot clock. The higher bandwidth requirements of 80-column text mode make this scheme fall apart.
CGA Snow
In high-resolution text mode, attempts to access video memory by the CPU while the CGA is rasterizing the active display area will cause a phenomenon called snow - random glitches will briefly appear on the screen, whenever the CGA happens to read data placed on its internal data bus by the CPU while it was attempting to read character glyphs or attribute bytes.
IBM worked around this in BIOS routines that scrolled the screen - such as when you execute the DIR command in DOS - by rapidly disabling and re-enabling the display, causing noticeable flicker. Some people find this flicker more annoying than the snow itself!
Primary References
- (seasip.info) Colour Graphics Adapter: Notes
- (int10h.org) The IBM 5153’s True CGA Palette and Color Output
- (int10h.org) 8088 MPH Final: Old vs. New CGA (and Other Gory Details)
- (int10h.org) CGA in 1024 Colors - a New Mode: the Illustrated Guide
- (github.org) IBM Colour Graphics Adapter schematics redrawn in KiCad
- (nerdlypleasures.blogspot.com) CGA 16 Color RGB Graphics Modes
Additional Reading
- (reenigne.org) CRTC emulation for MESS
- (martypc.blogspot.com) Exploring CGA Wait States
Reference Implementations
- (github.com) A digital logic simulation of the IBM CGA card
-
VileR, Name the Non-Standard PC Code Page, August 7, 2024. ↩
-
VileR, CGA in 1024 Colors - a New Mode: the Illustrated Guide, April 2015. ↩