Saturn VDP1 Command Tables: Drawing Commands and Framebuffer Handoffs
Trace Saturn VDP1 command lists from VRAM through drawing and END control to framebuffer switching, then diagnose synchronization faults with VDP2 in view.
The Sega Saturn VDP1 is a command-driven drawing processor. The SH-2 software builds command structures in VDP1-accessible VRAM, points the processor at a list, and asks it to draw sprites or polygons into a framebuffer. VDP2 then participates in producing the display image, so a correct VDP1 command stream is only one stage of the final picture. When an emulator shows missing primitives, stale objects, or a one-frame delay, inspect command execution and framebuffer ownership before blaming VDP2 priorities.
The VDP1 User’s Manual describes command formats, system registers, drawing operations, and framebuffer switching modes. The central architectural rule is that software, VDP1, and VDP2 do not all read and write one ordinary linear framebuffer in the same way. VDP1 consumes its list and writes through its drawing side; the display side is selected and switched according to the framebuffer control protocol. Command memory, texture/pattern memory, and display buffers share constrained VDP1 VRAM resources and must be laid out deliberately.
A command list is data consumed by a device
The command table is a sequence of hardware-format entries in VRAM. Commands describe a primitive and its parameters, including coordinates, texture or character pattern references, color mode, clipping, and shading where applicable. The manual defines command types and control fields; software should use a structure whose field widths and alignment match that definition rather than relying on a compiler’s default packed structure.
The first debugging task is to verify the list address, every command address, and the link/termination behavior expected by the command format. The VDP1 has an END command that ends drawing. Do not assume that a CPU-side array length automatically stops hardware traversal. A bad next link can send VDP1 into texture bytes or uninitialized RAM and create apparently random geometry.
A safe command builder maintains an explicit count, reserves a terminal command, checks that every link remains within allocated command memory, and verifies all pattern addresses against the VRAM map. Before submission, dump command words in the exact order the hardware will fetch them. A host-side debugger view of a C structure is not enough if byte order or field packing differs from the device format.
Drawing order and state are observable
VDP1 processes commands in list order and draws into the selected drawing framebuffer. Primitive order can therefore change the image when later primitives cover earlier pixels. VDP1 command attributes also define how source pattern pixels are interpreted, whether pixels are transparent, and which color/palette path is used. A texture that appears wrong can result from the character format, color calculation flags, clipping, or the destination pixel mode rather than a bad command pointer.
Use small deterministic lists to validate one operation at a time: a flat polygon, a scaled sprite, a textured quad, then clipping and Gouraud shading. Initialize every relevant command field, including reserved values as directed by the manual. Do not inherit undefined bytes from an old command list and then interpret one game title’s tolerance as hardware behavior.
VDP1 drawing completion is asynchronous relative to the SH-2. A CPU may prepare a later list or update memory while the VDP1 is drawing, but the legal overlap depends on ownership and the memory region being accessed. The manual’s system-control register and command behavior define the boundaries. An emulator should model completion and bus-visible status rather than rendering the whole list instantly on the submission write.
Framebuffer switching is a separate state machine
The framebuffer control register, FBCR, controls drawing/display switching and interlace-related modes. In normal cycle mode, the documented design uses a front buffer for display and a back buffer for drawing, then changes roles at a field boundary. The display buffer is not simply whichever buffer the CPU most recently wrote. The manual warns that reset values are undefined and requires software to initialize the switching mode.
Manual switching modes give software control over erase and change operations. These operations have ordering requirements across fields: erase can clear the display framebuffer, while change selects the other frame for display. Erasing without following with a change can leave a blank visible buffer; changing without erasing can reveal stale pixels. For interlaced modes, the manual distinguishes fields and double-density line behavior, so treating every callback as a complete progressive frame loses the field sequence.
Model FBCR writes at the specified field transition. A write is not necessarily effective at the exact host instruction that stores it; the manual describes internal update timing around field changes. Keep requested control bits separate from active front/back assignment so that an in-flight write does not retroactively alter a frame already being scanned.
The visible image pipeline also involves VDP2. VDP2 can use VDP1 output as a composition source and combine it with background planes, priorities, and color calculation. VDP1 should report its rendered-buffer state and identity to the rest of the machine. VDP2 should consume the buffer selected by the modeled system state, not whichever host texture was drawn most recently.
VRAM layout and alignment
VDP1 VRAM holds command tables, character patterns, and framebuffer data. The manual specifies address regions and restrictions. Calculate each allocation from the exact mode and buffer configuration, align it to the required boundaries, and reject overlap when building the display list. A compact scene can appear to work while a larger list overwrites a texture or framebuffer in the same region.
Separate allocation metadata by purpose. Track command bytes, pattern/texture bytes, framebuffer range, color lookup data, and any table referenced by commands. For every address, record whether the CPU, VDP1, or VDP2 can access it in the current state. A pointer that is legal for a SH-2 CPU read is not automatically a legal VDP1 command address.
When an emulator uses host GPU memory, preserve the device-visible address model. Translate VDP1 addresses through the emulated VRAM map and keep host resource IDs private to the renderer. Save states should serialize emulated VRAM and command/framebuffer control state, not native graphics handles.
Failure patterns and instrumentation
Missing or duplicated objects often trace to list links, END placement, or buffer swaps. Corrupted textures can come from command address calculation or VRAM overlap. A screen that alternates between new and stale content can result from front/back role confusion, an erase/change sequence that skipped a field boundary, or VDP2 sampling the wrong VDP1 frame. A render-only implementation that always writes to one host texture can conceal all three defects.
Log the list start address, command index, command type, decoded source and destination ranges, clipping mode, framebuffer selected for drawing, field number, FBCR requested and active state, draw completion, and VDP2 source selection. Include the exact Saturn video mode and interlace status. Keep traces bounded and capture the first invalid address or state transition rather than dumping every pixel.
A useful test matrix covers: clean reset with FBCR initialization, empty list ending immediately, one primitive, multiple overlapping primitives, a list near the allocated boundary, manual erase/change, normal double buffering, and each supported interlace path. Save and restore state while drawing is pending and after a completed draw; verify that the next visible field matches a deterministic baseline.
Avoid false simplifications
VDP1 is not a modern command buffer with an implicit fence at every host frame. Its device status, field timing, memory layout, and command table define synchronization. Likewise, a software queue that drains all commands before every frame is not equivalent to hardware if the SH-2 can observe an intermediate status or if VDP1 drawing overlaps other device work.
Do not infer framebuffer behavior from the names front and back alone. In manual modes, software controls erase and change timing. The frame-buffer change register has mode bits and trigger semantics; its state is not a boolean swap flag. Avoid clearing a host texture automatically at frame start unless the emulated erase operation says to do so.
Acceptance criteria
A production implementation parses commands from emulated VRAM, validates END and link progression, observes the actual drawing buffer and completion state, applies FBCR changes at documented field boundaries, and exposes the resulting frame to VDP2 using the correct buffer identity. Its logs can reproduce an invalid command address or a missed swap without depending on a host GPU screenshot.
VDP1 list execution and framebuffer control are two linked but independent contracts. Keeping them explicit makes rendering bugs diagnosable and prevents display composition from hiding an earlier device-state error.
Related:
- Sega Saturn VDP2: Scroll Planes, Priorities, and Color Calculation
- Sega Saturn SCU DMA: Transfer Levels, Indirect Tables, and Bus Boundaries
Sources: