Skip to content
FreeDOSDeep Dive Published Updated 8 min readViews unavailable

VGA Planar Graphics: Read Modes, Write Modes, and the Four Data Latches

Program VGA planar pixels safely by understanding the four data latches, graphics-controller read modes, write modes, bit masks, and plane ownership.

VGA planar graphics are not a linear array of color bytes. A CPU access to the VGA memory aperture passes through the Sequencer and Graphics Controller, which select planes, load internal latches, apply Boolean operations, and decide which bits reach video memory. That machinery can make a 16-color pixel operation efficient, but it also means a C pointer store is not automatically a complete pixel update.

This article focuses on the IBM VGA-compatible planar path, particularly the Graphics Controller read and write modes used by standard 16-color graphics. It assumes a program or driver intentionally owns the adapter. It is not a portable API for ordinary FreeDOS applications, and a clone, emulator, or extended VGA implementation must be checked against its own register documentation.

The memory model: four planes behind one aperture

In planar modes, a logical pixel’s color is represented by one bit in each of four bit planes. The four bits together select one of sixteen palette indexes. The planes are not four independently addressed windows visible through four C pointers. CPU addresses are decoded through the current VGA map, and the Sequencer Map Mask controls which planes can be written. The Graphics Controller’s read-map selection and mode registers control how reads and writes are interpreted.

For one byte offset, each plane stores eight adjacent pixel bits. A CPU read from the aperture causes the VGA to fetch the corresponding byte from all four planes into four internal data latches. In read mode 0, the host receives the byte from the selected read map. In read mode 1, the controller compares the latched plane values with the Color Compare and Color Don’t Care registers and returns a bit mask indicating which pixel positions match. The latter is useful for color searching, but it is not a normal byte load.

The latches are a key part of the write path. They hold the values fetched by the most recent video-memory read. A write may combine those latch bits with CPU data and register state. A routine that assumes the latches contain a known previous value without first performing the intended read can copy stale state from another part of the application or another driver.

What the four write modes do

Write mode 0 is the common flexible path. The controller rotates the CPU data as configured, optionally replaces it with Set/Reset values for enabled planes, applies a logical operation such as replace or XOR against the latches, and uses the Bit Mask to choose which bits are updated. Map Mask then determines which planes receive the result. A normal opaque color write sets all eight bits of the Bit Mask and chooses plane values from the four bits of the color index. A masked pixel write needs a bit mask that preserves neighboring pixels in the same byte.

Write mode 1 transfers the four latched bytes to the selected destination without deriving new pixel values from the CPU data. This is the hardware’s latch-copy operation and can accelerate a compatible screen-to-screen copy. The source read must populate the latches first, and the destination write must use the intended plane mask and addressing state. It is not a general-purpose memcpy: a mismatch in source and destination byte alignment or bit position requires additional work.

Write mode 2 expands the low four bits of CPU data into per-plane all-zero or all-one bytes, then combines them with the latches and Bit Mask. It is useful for writing a color index to selected pixel positions. Write mode 3 uses Set/Reset as the source color and the rotated CPU data combined with the Bit Mask as a mask. It can simplify some masked drawing operations, but its exact interaction with the operation and plane masks must be derived from the register definition rather than guessed.

The important engineering point is that mode names do not describe high-level drawing primitives. Each mode is a data path. Before selecting one, specify the desired result for all four planes, the source of each candidate bit, the bits to preserve, and the planes to enable. Then test that truth table for all sixteen color indexes and each relevant pixel position.

A deliberate masked-pixel algorithm

For a conventional four-plane pixel setter, first compute the byte offset from the row stride and horizontal position, then compute the bit mask for that pixel. The display mode establishes the stride and addressing convention; a VBE mode may report a pitch and layout that differs from a legacy VGA mode. The outline below describes intent rather than a drop-in port routine:

offset = y * planar_stride_bytes + (x / 8)
mask   = 0x80 >> (x % 8)

read one byte from the selected VGA memory aperture at offset
    # loads all four plane bytes into VGA latches
configure the documented write mode, set/reset color, bit mask, and map mask
write the destination byte to the same aperture offset
restore the register state that the caller's contract requires

The order matters: the read loads latches, and the subsequent write uses the same byte position. A typical routine configures Set/Reset from the four-bit color, enables Set/Reset for the planes it intends to affect, selects the replace operation, sets the one-pixel Bit Mask, enables all four planes in Map Mask, reads the destination, and then writes a dummy CPU value. Because register combinations and mask details are easy to get wrong, validate this sequence against the IBM VGA manual and a truth-table test before using it in a renderer.

Do not calculate y * 320 + x for a planar mode. The horizontal coordinate is divided into a byte address and a bit position. A mode’s pitch, start address, display origin, and split-screen settings can alter the visible relation between logical coordinates and aperture offsets. Keep pixel geometry separate from the low-level address translator.

Register access is shared state

Graphics Controller and Sequencer registers are selected through index/data port pairs. The adapter also contains the Attribute Controller’s separate address/data flip-flop. A rendering function that changes one register can silently change how later text output, BIOS services, or another resident utility behaves. A local save-and-restore of only the register directly used is insufficient if the selected mode depends on related state such as memory mapping, chain mode, read mode, Bit Mask, Set/Reset, or Map Mask.

The safest ownership boundary is a display driver that initializes the complete mode and mediates all drawing. A one-off DOS application can also own the adapter if it saves a documented state image, prevents conflicting software from touching it, and restores state on every normal exit. Even then, Ctrl-C, a crash, power loss, or a nested TSR can interrupt cleanup. Do not promise transparent restoration if the program cannot control those cases.

When using BIOS mode set, query or establish the known mode before accessing the aperture. BIOS and VBE mode calls can configure the planes and controller for different formats. Changing an indexed register by direct port I/O can make later BIOS calls produce unexpected output because the BIOS may assume its own mode state remains intact.

Common failure signatures

If drawing one pixel changes its neighbors, the Bit Mask or packed-bit calculation is wrong. If a color has the wrong subset of bits, inspect Set/Reset enable, Set/Reset values, Map Mask, and the selected logical operation. If a solid fill repeats stale colors, inspect latch loading and whether the code inadvertently uses write mode 1. If reads return surprising values after a write, check the active read mode and Read Map Select rather than assuming the aperture is ordinary RAM.

If corruption appears only after another library call, compare VGA register state before and after that call. If one emulator works and another does not, record adapter model, emulated VGA features, BIOS, selected mode, stride, and every register your program relies on. Do not treat a compatible-looking screenshot as evidence that the same register sequence works on physical hardware.

A practical acceptance matrix

Start with a disposable graphics mode and draw a pattern that places different colors in adjacent bits of one byte. Verify all eight positions, then test the first and last byte of a row, the first and final visible rows, and the edge where the next row begins. Exercise all four write modes independently with a small source/destination pattern, checking the resulting four planes rather than only the composite display. For read mode 1, test known compare colors and masks and confirm the return byte’s bit positions.

Record the mode, reported pitch, memory map, selected aperture, register snapshots, and expected plane bytes. Include one case where a library or BIOS call runs between drawing operations to expose accidental assumptions about retained state. Run under each emulator or adapter in scope. This is a validation plan, not a claim that the example sequence has been executed on a DOS target in this environment.

Planar VGA becomes predictable once the aperture is treated as a register-controlled transfer port rather than a flat pixel array. Understand the source and destination of every bit, load the latches deliberately, constrain the enabled planes, and keep exclusive ownership of the state your drawing path changes.

Related:

Sources:

Comments