PC Engine VCE: Palette RAM, Color Clocks, and Mid-Frame Changes
Trace PC Engine VCE palette selection, 9-bit color conversion, indexed register access, color-clock modes, and safe raster-time diagnostics.
The PC Engine’s display is produced by two cooperating video chips. The HuC6270 Video Display Controller generates background/sprite pixel indices and raster timing; the HuC6260 Video Color Encoder (VCE) holds the color table, selects the output color clock, and converts those indices into the video signal. The VCE is not the sprite table DMA engine, and its palette RAM is not a second copy of the VDC’s tile or sprite memory. Keeping the boundary explicit is essential when diagnosing wrong colors, raster artifacts, or region/timing mismatches.
The VCE is programmable through a small set of I/O ports in the hardware bank. Its control register selects the color clock frequency, while index and data registers access the color table. The reference documentation describes a 9-bit palette index and 9-bit RGB components, with three bits each for green, red, and blue. A write to the color data register updates the table used by subsequent output; that means palette state can change during a frame rather than only at a vertical blank boundary.
Keep pixel identity separate from color value
The VDC produces an index representing a selected palette entry. The VCE uses that index to look up component values and drive the output. A renderer should therefore carry both the source index and the resolved color until the point where the palette lookup is known to occur. If the emulator expands every tile pixel to RGB at tile-cache creation time, a later VCE palette write cannot affect pixels that are already cached unless the cache key includes the palette version.
This index/color separation is useful for debugging. Log the VDC’s output index, palette table address, stored component bits, output clock mode, and final host color. If the index is correct but RGB differs, investigate palette RAM addressing and component expansion. If the index is wrong, inspect VDC layers, priorities, transparency, and sprite/background selection instead. An emulator can get a visually plausible screenshot while using an incorrect palette pipeline that breaks raster effects.
Palette entry width matters. The VCE register stores three-bit components; converting to 8-bit host channels requires a defined expansion policy. Bit replication is one common expansion for an n-bit channel, while linear scaling is another; choose the behavior verified for the target output and use it consistently. Do not add color-management, gamma, or CRT transformations before confirming that the raw encoded palette values are correct.
The indexed register pair is stateful
Software selects a color-table index using the index register, then accesses the color data register. The documented interface auto-increments the index after a high-byte access to color data. That detail means byte writes, word writes, and split operations cannot be collapsed without considering the actual bus access. A program that writes a sequence of colors may depend on the increment point and access width.
Represent the VCE address pointer and byte/word transaction state explicitly. Test writes to the low and high portions, repeated reads or writes, pointer wraparound, and an interrupted sequence. In a trace, record the raw I/O address and width, selected palette index before and after access, component value, and current scanline/dot. If the hardware manual defines behavior for invalid or partial accesses, model it as documented; do not borrow assumptions from a different console’s palette ports.
The timing mode is another piece of state. The VCE control register selects between the documented 5 MHz and 7 MHz color-clock modes. That choice affects display timing and the relationship between dot progression and system cycles. It should be applied to the raster timing model, not merely used to change a host shader’s pixel width. When a mode change occurs, determine from hardware behavior how the VDC clock and output cadence react rather than assuming a frame restart.
Mid-frame writes and visible artifacts
Because color data updates the palette used by the display, a write during active output can affect pixels later in the scan. The hardware documentation cautions that software should preferably wait for vertical sync to avoid visible snow on a monitor. That is a practical display-quality recommendation; it is not evidence that every mid-frame write is ignored. Games and demos may intentionally exploit raster-time changes, and an emulator must preserve the write’s position in emulated time.
The host renderer must not resolve all palette colors at the start of a frame if palette writes can alter later pixels. One approach is to snapshot palette versions at scanline boundaries; another is to record per-pixel palette state or palette-write events and replay them in order. The chosen representation should preserve the granularity the hardware exposes. If the VCE write changes output immediately, a scanline-only snapshot is insufficient for midline effects.
Avoid introducing host display tearing into the emulated model. The original monitor’s analog behavior and the host’s swap chain are different systems. First reproduce the digital index-to-color sequence and timing; then apply optional display filtering. Otherwise a host refresh artifact can be mistaken for VCE behavior.
A compact palette-write trace
For test output, use a record that distinguishes access from rendering:
{
"cycle": 32000,
"scanline": 40,
"dot": 120,
"operation": "palette_write",
"index_before": 17,
"rgb_3bit": [7, 2, 0],
"index_after": 17
}
This is a diagnostic schema, not a claim about the byte-level port sequence; the index-after value depends on access width and the documented increment rule. A fixture should include both single-entry and sequential writes, with explicit bus width, so the expected pointer state is unambiguous.
Test the color path in isolation
Use a diagnostic frame where every VDC pixel emits a known index. Fill palette entries with ramps and primary combinations, then verify the raw nine-bit components before host conversion. Test all index bits, component bit positions, auto-increment, table wraparound, and reads as well as writes. Repeat with both color-clock settings and record raster length and line timing against a trusted implementation or hardware capture.
Next test a palette change at vertical blank, during a scanline, and immediately before/after a pixel fetch boundary. Compare the index stream separately from the output color stream. Include save-state restoration between palette-index selection and data access, and during active display after a mid-frame write. Preserve palette RAM, current index, access phase, color-clock mode, raster position, and pending write timing.
The VCE is small but central to the final image. A clean model treats the VDC as the source of pixel identity and the VCE as the timed color-lookup and output-clock stage. That architecture makes palette changes testable and prevents a renderer cache from turning a dynamic palette into a static texture.
Palette-correctness fixtures should avoid starting with a recognizable commercial screen, because artistic assets can hide bit errors. A simple index ramp reveals address truncation; a component ramp reveals swapped RGB fields; alternating palette rows reveal increment mistakes; and one-pixel stripes show whether a midline write takes effect too early or too late. Include entries at the lowest and highest valid index and data values with every component bit independently set. Store expected raw component words as the test oracle and compare host-expanded color only in a second assertion. This two-stage check distinguishes hardware decoding from display color conversion. If the emulator supports palette or tile caches, include a cache invalidation test that changes one entry after a frame has already been rendered and confirms that future output uses the new value without mutating historical pixels already sent to the display queue.
Related:
- PC Engine VDC SATB DMA: Sprite Tables, VBlank Transfers, and Timing
- Fixing Incorrect Colors in an Emulator Without Hiding the Cause with a Shader
Sources: