Turbo Pascal BGI Tutorial: Dynamic Drivers, Linked Drivers, and Diagnostic Harnesses

C:\RETRO\DOS\TP>type turbop~3.htm

Turbo Pascal BGI Tutorial: Dynamic Drivers, Linked Drivers, and Diagnostic Harnesses

This tutorial gives you a practical BGI workflow that survives deployment:

  1. dynamic driver loading from filesystem
  2. linked-driver strategy for lower runtime dependency risk
  3. a minimal diagnostics harness for startup failures

Preflight: what you need

  • Turbo Pascal / Borland Pascal environment with Graph unit
  • one known-good BGI driver set and required .CHR fonts
  • a test machine/profile where paths are not identical to dev directories

TP5 baseline reminder:

  • compile needs GRAPH.TPU
  • runtime needs .BGI drivers
  • stroked fonts need .CHR files

Step 1: dynamic loading baseline

Create BGITEST.PAS:

program BgiTest;

uses
  Graph, Crt;

var
  gd, gm, gr: Integer;

begin
  gd := Detect;
  InitGraph(gd, gm, '.\BGI');
  gr := GraphResult;
  Writeln('Driver=', gd, ' Mode=', gm, ' GraphResult=', gr);
  if gr <> grOk then
    Halt(1);

  SetColor(15);
  OutTextXY(8, 8, 'BGI OK');
  Rectangle(20, 20, 200, 120);
  ReadKey;
  CloseGraph;
end.

Expected outcome:

  • with correct path/assets: startup succeeds and simple frame draws
  • with missing assets: GraphResult indicates error and program exits cleanly

Important TP5 behavior: GraphResult resets to zero after being called. Always store it to a variable once, then evaluate that value.

Path behavior detail: if InitGraph(..., PathToDriver) gets an empty path, the driver files must be in the current directory.

Step 2: deployment discipline for dynamic model

Package checklist:

  1. executable
  2. all required .BGI files for target adapters
  3. all required .CHR fonts
  4. documented runtime path policy

Most “BGI bugs” are missing files or wrong path assumptions.

Step 3: linked-driver strategy (when you need robustness)

Some Borland-era setups support converting/linking BGI driver binaries into object modules and registering them before InitGraph (for example through RegisterBGIdriver and related registration APIs).

General workflow:

  1. run BINOBJ on .BGI file(s) to get .OBJ
  2. link .OBJ file(s) into program
  3. call RegisterBGIdriver before InitGraph
  4. call InitGraph and verify GraphResult

Why teams did this:

  • fewer runtime file dependencies
  • simpler deployment to constrained/chaotic DOS installations

Tradeoff:

  • larger executable and tighter build coupling

Ordering constraint from TP5 docs: calling RegisterBGIdriver after graphics are already active yields grError (-11).

If you use InstallUserDriver with an autodetect callback, TP5 expects that callback to be a FAR-call function with no parameters returning an integer mode or grError.

Step 4: diagnostics harness you should keep forever

Keep a dedicated harness separate from game/app engine:

  • prints detected driver/mode and GraphResult
  • renders one line, one rectangle, one text string
  • exits on keypress

This lets you quickly answer: “is graphics stack alive?” before debugging your full renderer.

Add one negative test here too: intentionally pass wrong mode for a known driver and verify expected grInvalidMode (-10).

Step 5: test matrix (predict first, then run)

Define expected outcomes before running each case:

  1. correct BGI path
  2. missing driver file
  3. missing font file
  4. wrong current directory
  5. TSR-heavy memory profile

For each case, record:

  • startup status
  • exact error code/output
  • whether fallback path triggers correctly

Recommended TP5 error codes to classify in logs:

  • grNotDetected (-2)
  • grFileNotFound (-3)
  • grInvalidDriver (-4)
  • grNoLoadMem (-5)
  • grFontNotFound (-8)
  • grNoFontMem (-9)
  • grInvalidMode (-10)

Step 6: fallback policy for production-ish DOS apps

Never rely on detect-only logic without fallback:

  1. try preferred mode
  2. fallback to known-safe mode
  3. print actionable error if both fail

A black screen is a product bug, even in retro projects.

About creating custom BGI drivers

Writing full custom BGI drivers is advanced and depends on ABI/tooling details that are often version-specific and poorly documented. Practical teams usually ship stock drivers (dynamic or linked) unless there is a hard requirement for new hardware support.

If you must go custom, treat it as a separate reverse-engineering project with its own test harnesses and compatibility matrix.

Integration notes with overlays and memory strategy

If graphics startup becomes unstable after enabling overlays:

  • verify overlay initialization order
  • verify memory headroom before InitGraph
  • test graphics harness independently from overlayed application paths

This avoids mixing two failure domains during triage.

Memory interaction note from TP5 docs:

  • Graph allocates heap memory for graphics buffer/driver/font paths
  • OvrSetBuf also reshapes memory by shrinking heap
  • call order matters (OvrSetBuf before InitGraph when both are used)

Related reading:

2026-02-22 | MOD 2026-03-09