Mode X in Turbo Pascal, Part 2: Primitives and Clipping

Mode X in Turbo Pascal, Part 2: Primitives and Clipping

After the planar memory model clicks, the next trap is pretending linear drawing code can be “ported” to Mode X by changing one helper. That works for demos and fails for games. Robust Mode X rendering starts with primitives that are aware of planes, clipping, and page targets from day one.

If you missed the foundation, begin with Part 1: Planar Memory and Pages. This article assumes you already have working pixel output and page flipping.

Primitive design goals

For old DOS rendering pipelines, primitives should optimize for correctness first:

  1. Never write outside page bounds.
  2. Keep clipping deterministic and centralized.
  3. Minimize per-pixel register churn where possible.
  4. Separate addressing math from shape logic.

Performance matters, but undefined writes kill performance faster than any missing micro-optimization.

Clipping is policy, not an afterthought

A common beginner pattern is “draw first, check later.” On VGA memory that quickly becomes silent corruption. Instead, apply clipping at primitive boundaries before entering the hot loops.

For axis-aligned boxes, clipping is straightforward:

function ClipRect(var X1, Y1, X2, Y2: Integer): Boolean;
begin
  if X1 < 0 then X1 := 0;
  if Y1 < 0 then Y1 := 0;
  if X2 > 319 then X2 := 319;
  if Y2 > 199 then Y2 := 199;
  ClipRect := (X1 <= X2) and (Y1 <= Y2);
end;

Once clipped, your inner loop can stay simple and trustworthy. This is less glamorous than fancy blitters and infinitely more important.

Horizontal fills with reduced state changes

Naive pixel-by-pixel fills set map mask every write. Better approach: process spans in groups where plane mask pattern repeats predictably. Even a modest rework reduces I/O pressure.

procedure HLineX(X1, X2, Y: Integer; C: Byte);
var
  X: Integer;
begin
  if (Y < 0) or (Y > 199) then Exit;
  if X1 > X2 then begin X := X1; X1 := X2; X2 := X; end;
  if X1 < 0 then X1 := 0;
  if X2 > 319 then X2 := 319;

  for X := X1 to X2 do
    PutPixelX(X, Y, C);
end;

This still calls PutPixelX, but with clipping discipline built in. Later you can specialize spans and batch by plane.

Rectangle fills and UI panels

Old DOS interfaces often combine world rendering plus overlays. A clipped rectangle fill is the workhorse for panels, bars, and damage flashes.

procedure FillRectX(X1, Y1, X2, Y2: Integer; C: Byte);
var
  Y: Integer;
begin
  if not ClipRect(X1, Y1, X2, Y2) then Exit;
  for Y := Y1 to Y2 do
    HLineX(X1, X2, Y, C);
end;

It looks boring because good infrastructure often does. Boring primitives are stable primitives.

Line drawing without hidden chaos

For general lines, Bresenham remains practical. The Mode X-specific advice is to keep the stepping algorithm independent from memory layout and delegate write target handling to one consistent pixel primitive.

Why this matters: when bugs appear, you can isolate whether the issue is geometric stepping or planar addressing. Mixed concerns create mixed failures and bad debugging sessions.

Instrument your renderer early

Before moving to sprites, add a diagnostic frame:

  • draw clipped and unclipped test rectangles at edges
  • draw diagonal lines through all corners
  • render page index and frame counter
  • flash a corner pixel each frame

If this test scene is unstable, your game scene will be chaos with better art.

Structured pass order

A practical frame pipeline in Mode X might be:

  1. clear draw page
  2. draw background spans
  3. draw world primitives
  4. draw sprite layer placeholders
  5. draw HUD rectangles/text
  6. flip display page

This ordering gives deterministic overdraw and clear extension points for Part 3.

Cross-reference with existing DOS workflow

These graphics routines live inside the same operational reality as your boot and tooling discipline:

Old graphics programming is rarely “graphics only.” It is always an ecosystem of memory policy, startup profile, and debugging rhythm.

Next step

Part 3 moves from primitives to actual game-feeling output: masked sprites, palette cycling, and timing control:

Primitives are where reliability is born. If your clips are correct and your spans are deterministic, everything built above them gets cheaper to reason about.

One extra practice that helps immediately is recording a tiny “primitive conformance” script in your repo: expected screenshots or checksum-like pixel probes for a fixed test scene. Run it after every renderer change. In retro projects, visual regressions often creep in from seemingly unrelated optimizations, and this one habit catches them early.

2026-02-22